django数据库查询优化

时间:2011-11-30 14:06:47

标签: django django-orm database-optimization

关于django db查询优化的新手问题:

我有一个自定义模型表单来编辑Destination对象,我在构造函数中从相关的Visitor模型中获取了一个查询集,该模型将ManyToMany字段放入Destination(请参阅EDIT以了解使用自定义模型表单的原因) )

    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"

我们的想法是将相关的visitor_set加载到变量中,以避免重复查询以检查visitor_set中是否存在每个访问者。这是最好的方法吗?

另外,如果我打开数据库日志记录(如本question中所述,第二个答案),我可以看到重复查询(第3个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对象是ManyToMany对象上Visitor字段的相关方。如果我的表单正在编辑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
>>>

由于

1 个答案:

答案 0 :(得分:1)

回答你说的是你的问题:我认为是查询

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,)

来自

if obj in visitor_set:

其中Django重新执行visitor_set的查询(请参阅Django文档中的When QuerySets are evaluated)。您可以通过立即将visitor_set转换为set来避免这种情况(因此Django必须立即执行查询),如:

visitor_set = set(self.instance.visitor_set.all())

这也将提高测试对象是否在此集合中的性能(与list或类似的可迭代对比)。