0

我试图了解在 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)

视图.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 语句时,我似乎都在访问数据库,例如 {% for c in cats %},(至少我从阅读 debug_toolbar 中相信如此)。我尝试过的其他方法以 (C x D x E) 数量的数据库命中而告终。这是我使用预取、查询或模型的固有问题吗?可以说,Django 访问具有“深度> 1”的数据库对象的最佳方法是什么?

4

2 回答 2

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',)
于 2013-03-20T02:11:55.653 回答
0

所以,我发现这里发生了一些事情:

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

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

prefetch_related() 对每个关系进行单独的查找并将它们连接到 python 中,并且是用于多对多、多对一以及 GenericRelation 和 GenericForeignKey 的方法。

按照书,我应该使用 prefetch(),因为我没有“遵循”外键。这就是我对此的理解,但是在评估模板中给定的 for 循环时,我的模板似乎引起了额外的查询,即使我添加了 {with} 标记的使用。

起初,我以为我发现了与这个问题类似的东西,但是当我构建我的简化示例时,我无法复制。我从使用调试工具栏切换到使用以下模板代码直接检查(在 Karen Tracey 的文章 Tracking SQL Queries for a Request using Django 中,我会链接但链接受限):

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

使用这种方法,我只看到使用 pre-fetch() 的 5 个查询(使用 debug_toolbar 的 7 个),并且在使用 select_related() 时查询线性增长(对于 debug_toolbar 使用 +2),我相信这是预期的。

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

于 2013-03-20T20:07:18.123 回答