预取manytomany字段不会改变执行速度

时间:2019-09-08 18:58:26

标签: python django django-orm

m = MyModel.objects.all().only("colA", "colB").prefetch_related("manyToManyField")

for mm in m:
    print(mm.id)
    list(mm.manyToManyField.values_list('id', flat=True))

此代码执行时间太长。

这几乎不需要时间(循环中没有对manyToManyField的引用):

m = MyModel.objects.all().only("colA", "colB").prefetch_related("manyToManyField")

for mm in m:
    print(mm.id)

这几乎与第一个完全一样

m = MyModel.objects.all().only("colA", "colB")

for mm in m:
    print(mm.id)
    list(mm.manyToManyField.values_list('id', flat=True))

这使我认为.prefetch_related("manyToManyField")毫无用处,实际上并没有获取任何东西,list(mm.manyToManyField.values_list('id', flat=True))在每个周期都访问数据库。

这是为什么,我怎么能强制从多方字段中预取?

我尝试删除list(),但是mm.manyToManyField.all().values_list给了我一个不是JSON可序列化的查询集(不,我不想安装rest框架)。

还尝试了list(mm.manyToManyField.all().values_list)list():仍然疯狂得很慢。

1 个答案:

答案 0 :(得分:2)

  

这是为什么,我怎么能强制从多方字段中预取?

发生这种情况的原因是因为您进行的查询与manyToManyField.all()不同,因此没有执行 。假设您会myManyToManyField.filter(some_col=some_val),那么它也会对数据库产生影响,因为已对数据库进行了优化以有效过滤。

如果要获取值,请使用:

# no extra query

for mm in m:
    print(list(mm.manyToManyField.all()))

或者如果您想打印主键,则可以通过列表理解来获取它们,例如:

# no extra query

for mm in m:
    print([k.id for k in mm.manyToManyField.all()])

它不会进行其他查询,因为您已经用.prefetch_related('manyToManyField')加载了该查询,但是未加载所有变体,例如过滤,注释等。

不过,您可以传递任意查询集以使用Prefetch objects [Django-doc]进行预取。例如,如果您要检索.values_list('id'),则可以使用以下方法进行预取:

from django.db.models import Prefetch

m = MyModel.objects.only("colA", "colB").prefetch_related(
    Prefetch(
        'myManyToManyField',
        queryset=TargetModel.objects.filter(pk__gt=5),
        to_attr='filtered_pks'
    )
)

然后,由此产生的MyModel将在此处具有一个额外的属性'filtered_pks',其中包含该相关模型的.filter(pk__gt=5)。因此,TargetModelManyToManyField所引用的模型。