2011-11-30 107 views
1

在Django的数据库查询优化新手问题:Django的数据库查询优化

我有一个自定义模式表单编辑一个目标对象,我在构造函数中,我得到一个QuerySet的持有从相关访客模式,具有多对多字段到目的地(见编辑使用自定义模型形式的原因)

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 

EDIT

Destination的对象我指的是一个ManyToMany场的Visitor物体上的相关侧。如果我的表单正在编辑Visitor对象本身,那么Django会自动处理ManyToMany字段。但要在Destination的模型表单上执行此操作,我需要为Visitor添加多选字段,并自定义__init__方法以加载它的选项和初始选择。

问题不过是关于如何处理查询集,以及神秘的第二个SQL加载多对多值,我也可以从外壳看到:

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

你能解释你想达到什么吗?对我来说,如果你为其中'visitor'是'ManyToManyField'的模型生成一个'ModelForm',你就会默认做django应该做的事情? –

+0

是的,你是对的,我的表单试图保存一个'目的地'模型,它位于我的'访问者'模型的ManytoMany字段的相关端,我将发布模型来澄清。所以我可以有一个表格来保存'访问者',它会自动处理ManyToMany,但是我想要探索这种做法。然而,我的问题是关于我处理查询集的方式,以及我在日志中看到的奇怪的附加sql语句 – xuloChavez

回答

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_setset马上(这样的Django被迫立即执行查询),如:

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

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