1

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

我有一个自定义模型表单来编辑 Destination 对象,并且我在构造函数中获取了来自相关访问者模型的查询集,该模型在 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"

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

另外,如果我打开数据库日志记录(如本问题中所述,第二个答案),我可以看到一个重复的查询(第三个 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。如果我的表单正在编辑访问者对象本身,那么 Django 将ManyToMany自动处理该字段。但是要在模型表单上执行此操作,Destination我需要为它添加一个多项选择字段Visitor并自定义__init__方法来为其加载选择和初始选择。

然而,问题是关于如何处理查询集,以及神秘的第二个 sql 来加载许多值,我也可以从 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
>>>

谢谢

4

1 回答 1

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(请参阅When QuerySets are evaluate in the Django docs)。您可以通过立即转换visitor_set为 a来避免这种set情况(因此 Django 被迫立即执行查询),如下所示:

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

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

于 2011-12-14T10:02:08.890 回答