【问题标题】:django best practice query foreign keydjango最佳实践查询外键
【发布时间】:2013-03-20 00:55:59
【问题描述】:

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

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

【问题讨论】:

    标签: django


    【解决方案1】:

    使用 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',)
    

    【讨论】:

      【解决方案2】:

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

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

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

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

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

      起初,我以为我发现了类似于 this issue 的东西,但是当我构建我的简化示例时,我无法复制。我从使用调试工具栏切换到使用以下模板代码直接检查(在 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() 时查询线性增长(使用 +2 的 debug_toolbar),我相信这是预期的。

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

      【讨论】:

        猜你喜欢
        • 2011-06-29
        • 1970-01-01
        • 1970-01-01
        • 2013-05-10
        • 1970-01-01
        • 2013-10-02
        • 2013-01-06
        • 1970-01-01
        • 2010-12-18
        相关资源
        最近更新 更多