Django数据库查询优化
关于 django db 查询优化的新手问题:
我有一个自定义模型表单来编辑 Destination 对象,并且在构造函数中我从相关的 Visitor 模型中获取查询集,该模型在 Destination 中具有 ManyToMany 字段(请参阅编辑了解原因使用自定义模型表单)
print "loading initial choices"
visitor_choices, visitor_initial = [], []
visitor_set = self.instance.visitor_set.all()
print visitor_set
for obj in Visitor.objects.all():
visitor_choices.append((obj.pk, obj.name))
#if visitor_set.filter(pk=obj.pk # this hits the db every time!
if obj in visitor_set:
visitor_initial.append(obj.pk)
self.fields['visitors'].choices = visitor_choices
self.fields['visitors'].initial = visitor_initial
print "finished loading initial choices"
这个想法是将相关的访问者集加载到变量中,以避免重复查询以检查访问者集中是否存在每个访问者。这是最好的方法吗?
另外,如果我打开数据库日志记录(如问题中所述,第二个答案),我可以看到一个重复的查询(第三个 SELECT 语句)来选择目的地 id 1 的所有访问者,但这在我编写的代码中没有,它来自哪里?
loading initial choices
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testapp_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 LIMIT 21; args=(1,
)
[<Visitor: MIMA>, <Visitor: MIMO>, <Visitor: MIMU>]
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor"; args=()
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testapp_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 ; args=(1,)
finished loading initial choices
编辑
我引用的Destination
对象是Visitor
对象上ManyToMany
字段的相关端。如果我的表单正在编辑 Visitor 对象本身,那么 Django 将自动处理 ManyToMany
字段。但要在 Destination
的模型表单上执行此操作,我需要为 Visitor
添加多项选择字段,并自定义 __init__
方法来加载选择以及它的初始选择。
然而问题是如何处理查询集,以及神秘的第二个sql来加载manytomany值,我也可以从shell中看到:
>>> from testapp.forms import DestinationForm
>>> from testapp.models import Destination, Visitor
>>> dest = Destination.objects.get(pk=1)
(0.001) SELECT "testapp_destination"."id", "testapp_destination"."destination" FROM "testapp_destination" WHERE "testapp_destination"."id" =
1 ; args=(1,)
>>> destinationForm = DestinationForm(instance=dest)
loading initial choices
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testap
p_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 LIMIT 21; args=(1,
)
[<Visitor: MIMA>, <Visitor: MIMO>, <Visitor: MIMU>]
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor"; args=()
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testap
p_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 ; args=(1,)
finished loading initial choices
>>>
谢谢
a newbie question on django db query optimisation:
I have a custom model form to edit a Destination object, and I in the constructor I get hold of a queryset from a related Visitor model, which has ManyToMany field into Destination(see EDIT for the reason to use a custom model form)
print "loading initial choices"
visitor_choices, visitor_initial = [], []
visitor_set = self.instance.visitor_set.all()
print visitor_set
for obj in Visitor.objects.all():
visitor_choices.append((obj.pk, obj.name))
#if visitor_set.filter(pk=obj.pk # this hits the db every time!
if obj in visitor_set:
visitor_initial.append(obj.pk)
self.fields['visitors'].choices = visitor_choices
self.fields['visitors'].initial = visitor_initial
print "finished loading initial choices"
The idea was to load the related visitor_set into a variable, to avoid repeated queries to check if each visitor is present in the visitor_set. Is this the best approach?
Also, if I switch on db logging (as explained in this question, second answer), I can see a repeated query (the 3rd SELECT statement) to select all visitors for destination id 1, but this is nowhere in the code I wrote, where does it come from?
loading initial choices
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testapp_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 LIMIT 21; args=(1,
)
[<Visitor: MIMA>, <Visitor: MIMO>, <Visitor: MIMU>]
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor"; args=()
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testapp_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 ; args=(1,)
finished loading initial choices
EDIT
The Destination
object I refer to is the related side of a ManyToMany
field on a Visitor
object. If my form was editing the Visitor object itself then Django would handle the ManyToMany
field automatically. But to do that on the model form for Destination
I need to add a multiple choice field for Visitor
and customize the __init__
method to load the choices and initial selections for it.
The question however is about how to handle the queryset, and the mysterious 2nd sql to load the manytomany values, which I can also see from the shell:
>>> from testapp.forms import DestinationForm
>>> from testapp.models import Destination, Visitor
>>> dest = Destination.objects.get(pk=1)
(0.001) SELECT "testapp_destination"."id", "testapp_destination"."destination" FROM "testapp_destination" WHERE "testapp_destination"."id" =
1 ; args=(1,)
>>> destinationForm = DestinationForm(instance=dest)
loading initial choices
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testap
p_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 LIMIT 21; args=(1,
)
[<Visitor: MIMA>, <Visitor: MIMO>, <Visitor: MIMU>]
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor"; args=()
(0.000) SELECT "testapp_visitor"."id", "testapp_visitor"."name" FROM "testapp_visitor" INNER JOIN "testapp_visitor_destinations" ON ("testap
p_visitor"."id" = "testapp_visitor_destinations"."visitor_id") WHERE "testapp_visitor_destinations"."destination_id" = 1 ; args=(1,)
finished loading initial choices
>>>
Thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
回答你所说的是你的问题:我认为查询
来自
Django 重新执行
visitor_set
查询的行(参见 当 Django 文档中的 QuerySet 被评估时)。您可以通过立即将visitor_set
转换为set
来避免这种情况(因此 Django 被迫立即执行查询),如下所示:这也将提高测试是否存在的性能一个对象位于该集合中(与
list
或类似的可迭代对象相比)。To answer what you say is your question: I think the query
comes from the line
where Django re-executes the query of
visitor_set
(see When QuerySets are evaluated in the Django docs). You can avoid that by convertingvisitor_set
to aset
right away (so Django is forced to execute the query immediately), as in:This will also improve the performance of testing whether an object is in this set (in comparison to a
list
or similar iterable).