预取 manytomany 字段不会改变执行速度
Prefetching manytomany field doesn't change execution speed
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))
每个周期都会访问数据库。
为什么会这样,我如何强制从 manytomany 字段中预取?
我试图删除 list()
但 mm.manyToManyField.all().values_list
给了我一个不可 JSON 序列化的查询集(不,我不想安装 rest 框架)。
还尝试了 list(mm.manyToManyField.all().values_list)
和 list()
:仍然很慢。
Why is this and how can I force to prefetch from a manytomany field?
发生这种情况的原因是因为您进行了与 manyToManyField.all()
不同的查询,因此 未执行 。想象一下,如果您 myManyToManyField.filter(some_col=some_val)
,那么它也会访问数据库,因为数据库经过优化可以有效过滤。
如果要获取值,请使用:
# no extra query
for mm in m:
print(list(mm.manyToManyField<b>.all()</b>))
或者如果你想打印主键,你可以用列表理解来获取这些主键,例如:
# no extra query
for mm in m:
print([<b>k.id</b> for k in mm.manyToManyField.all()])
它不会进行额外的查询,因为您已经用 .prefetch_related('manyToManyField')
加载了那个查询,但是所有变体,如过滤、注释等都没有加载。
然而,您可以传递任意查询集以使用 Prefetch
objects [Django-doc] 进行预取。例如,如果你想检索 .values_list('id')
,你可以预取它:
from django.db.models import <b>Prefetch</b>
m = MyModel.objects.only("colA", "colB").prefetch_related(
<b>Prefetch(</b>
'myManyToManyField',
<b>queryset=</b>TargetModel.objects.<b>filter(pk__gt=5)</b>,
to_attr='filtered_pks'
)
)
那么由此产生的MyModel
在这里会有一个额外的属性'filtered_pks'
,它包含那个相关模型的.filter(pk__gt=5)
。 TargetModel
因此是 ManyToManyField
所指的模型。
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))
每个周期都会访问数据库。
为什么会这样,我如何强制从 manytomany 字段中预取?
我试图删除 list()
但 mm.manyToManyField.all().values_list
给了我一个不可 JSON 序列化的查询集(不,我不想安装 rest 框架)。
还尝试了 list(mm.manyToManyField.all().values_list)
和 list()
:仍然很慢。
Why is this and how can I force to prefetch from a manytomany field?
发生这种情况的原因是因为您进行了与 manyToManyField.all()
不同的查询,因此 未执行 。想象一下,如果您 myManyToManyField.filter(some_col=some_val)
,那么它也会访问数据库,因为数据库经过优化可以有效过滤。
如果要获取值,请使用:
# no extra query
for mm in m:
print(list(mm.manyToManyField<b>.all()</b>))
或者如果你想打印主键,你可以用列表理解来获取这些主键,例如:
# no extra query
for mm in m:
print([<b>k.id</b> for k in mm.manyToManyField.all()])
它不会进行额外的查询,因为您已经用 .prefetch_related('manyToManyField')
加载了那个查询,但是所有变体,如过滤、注释等都没有加载。
然而,您可以传递任意查询集以使用 Prefetch
objects [Django-doc] 进行预取。例如,如果你想检索 .values_list('id')
,你可以预取它:
from django.db.models import <b>Prefetch</b>
m = MyModel.objects.only("colA", "colB").prefetch_related(
<b>Prefetch(</b>
'myManyToManyField',
<b>queryset=</b>TargetModel.objects.<b>filter(pk__gt=5)</b>,
to_attr='filtered_pks'
)
)
那么由此产生的MyModel
在这里会有一个额外的属性'filtered_pks'
,它包含那个相关模型的.filter(pk__gt=5)
。 TargetModel
因此是 ManyToManyField
所指的模型。