django最佳实践查询外键

时间:2013-03-20 00:55:59

标签: django

我试图了解在django中构建查询的最佳方法,以避免过多的数据库命中 这与问题类似:Django best practice with foreign key queries,但在查询中涉及更大的“深度”。

我的情况: models.py

class Bucket(models.Model):
    categories = models.ManyToManyField('Category')

class Category(models.Model):
    name = models.CharField(max_length=50)

class SubCategory(models.Model):
    category = models.ForeignKey(Category)

class SubSubCategory(models.Model):
    subcat = models.ForeignKey(SubCategory)

views.py

def showbucket(request, id):
    bucket = Bucket.objects.prefetch_related('categories',).get(pk=id)
    cats = bucket.categories.prefetch_related('subcategory_set__subsubcategory_set',)
    return render_to_response('showbucket.html', locals(), context_instance=RequestContext(request))

及相关模板:

{% for c in cats %}
    {{c}}
    <ul>
    {% for d in c.subcategory_set.all %}
        <li>{{d}}</li>
        <ul>
        {% for e in d.subsubcategory_set.all %}
            <li>{{e}}</li>
        {% endfor %}
        </ul>
    {% endfor %}
    </ul>
{% endfor %}

尽管使用了prefetch_related(),但每次评估前两个语句时,我似乎都在点击数据库,例如{%for c in cats%},(至少我相信阅读debug_toolbar)。我尝试过的其他方式最终得到了(C x D x E)数据库命中数。这是我使用预取,查询或模型本质上是错误的吗? Django访问具有“深度&gt; 1”的数据库对象的最佳方式是什么?

2 个答案:

答案 0 :(得分:3)

改为使用select_related():

https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.select_related

bucket = Bucket.objects.select_related('categories',).get(id=id)
cats = bucket.categories.select_related('subcategory_set__subsubcategory_set',)

答案 1 :(得分:0)

所以,我发现这里有几件事情发生了:

首先,我目前对select_related vs prefetch_related的理解:

select_related()遵循外键关系,导致更大的结果集,但意味着以后使用FK不需要额外的数据库命中。它仅限于FK和一对一的关系。

prefetch_related()对每个关系进行单独查找并在python中连接它们,并且意味着用于多对多,多对一,GenericRelation和GenericForeignKey。

通过这本书,我应该使用prefetch(),因为我没有“关注”外键。 这就是我理解的内容,但是我的模板似乎在评估模板中给定的for循环时引起了额外的查询,即使我添加了{with}标签的使用。

起初,我以为我发现了类似于this issue的东西,但是当我构建我的简化示例时,我无法复制。我使用调试工具栏切换到使用以下模板代码进行直接检查(在Karen Tracey使用Django跟踪SQL查询的文章中,我会链接但是链接限制):

{% with sql_queries|length as qcount %}
{{ qcount }} quer{{ qcount|pluralize:"y,ies" }}
{% for qdict in sql_queries %}
{{ qdict.sql }} ({{ qdict.time }} seconds)
{% endfor %}
{% endwith %}

使用这种方法,我只看到5个使用pre-fetch()的查询(带有debug_toolbar的7个),并且当使用select_related()(对于debug_toolbar使用+2)时,查询会线性增长,我认为这是预期的。 / p>

我很乐意接受任何其他建议/工具来处理这些类型的问题。