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():仍然疯狂缓慢。

最佳答案

为什么会这样,我又该如何强制从多个领域进行预取?


发生这种情况的原因是因为您进行的查询与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所引用的模型。

关于python - 预取manytomany字段不会改变执行速度,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57844912/

10-12 22:03