【问题标题】:Django best practice with foreign key queries外键查询的 Django 最佳实践
【发布时间】:2011-06-29 03:27:56
【问题描述】:

models.py

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

class SubCatergory(models.Model):
    parent_category = models.ForeignKey(Category)
    name = models.CharField(max_length=100)

views.py

def all_products(request):
c = Category.objects.all()
s = SubCatergory.objects.all()

return render_to_response('all_products.html',
                          {'c':c, 's':s})

all_products.html

{% for category in c %}
    <h1>{{ category.name }}</h1>
    <ul>
        {% for sub in s  %}
        {% if category.id == sub.parent_category.id %}
            <li>{{ sub.name }}</li>
        {% endif %}
        {% endfor %}
    </ul>
{% endfor %}

只是想知道以上是否是外键查询的最佳实践。我在模板级别进行过滤(如果 category.id == sub...),我应该将其移动到模型级别还是视图级别?

【问题讨论】:

    标签: django django-models django-templates django-views


    【解决方案1】:

    如果子类别只有一个深度,则以下代码应该没有问题:

    {% for category in c %}
        <h1>{{ category.name }}</h1>
        <ul>
            {% for sub in category.subcatergory_set.all %}
                <li>{{ sub.name }}</li>
            {% endfor %}
        </ul>
    {% endfor %}
    

    但是有一些优化技巧可以减少查询次数,因为您将在每个循环中执行一次查询。我现在正在想一个。

    实际上,我开始认为这是一个有趣的问题:最佳实践?

    您的方法使用 2 个查询。我的方法是使用 django 实践,但会执行多个查询。

    为了防止多次查询,您基本上必须在视图中执行您在模板中所做的相同操作,即迭代 SubCatergory,在 python 中提取它们的 ID,并将每个 ID 集分组到一个属性上Category.

    我不知道这个问题的答案。

    【讨论】:

    • 谢谢,子类别只有一个深度。
    【解决方案2】:

    我认为这里的一个好的做法是为这项工作创建一个模板标签。这样你就可以缓存渲染的模板,并且只在第一次渲染时访问数据库。

    首先,在你的应用中创建模板标签

    模板标签/show_categories_list.py

    from django.core.cache import cache
    
    @register.simple_tag
    def show_categories_list():
        cached = cache.get('CATEGORIES_LIST_CACHE_KEY', None)
        if cached is None:
            categories = Category.objects.all()
            rendered = render_to_string('all_categories.html', {'categories': categories})
            cache.set('CATEGORIES_LIST_CACHE_KEY', rendered)
            return rendered
        return cached
    

    然后,创建要使用的模板

    all_categories.html

    {% for category in categories %}
        <h1>{{ category.name }}</h1>
        <ul>
            {% for sub in category.subcategory_set.all %}
                <li>{{ sub.name }}</li>
            {% endfor %}
        </ul>
    {% endfor %}
    

    覆盖模型中的 save 方法,以便删除类别列表缓存条目(强制它在下一个请求时呈现,这也可以放在 (pre|post)_save 信号处):

    models.py

    class Category(models.Model):
        name = models.CharField(max_length=50)
    
        def save(self, *args, **kwargs):
            cache.delete('CATEGORIES_LIST_CACHE_KEY')
            return super(Category, self).save(*args, **kwargs)
    
    class SubCatergory(models.Model):
        parent_category = models.ForeignKey(Category)
        name = models.CharField(max_length=100)
    
        def save(self, *args, **kwargs):
            cache.delete('CATEGORIES_LIST_CACHE_KEY')
            return super(Category, self).save(*args, **kwargs)
    

    最后像这样使用它:

    base.html

    {% load show_categories_list %}
    {% show_categories_list %}
    

    您还可以为缓存条目添加超时,因此您不必覆盖模型中的保存方法,但您必须等待超时才能再次渲染。

    一些有用的参考资料:

    http://docs.djangoproject.com/en/1.2/howto/custom-template-tags/#shortcut-for-simple-tags http://docs.djangoproject.com/en/1.2/topics/cache/#the-low-level-cache-api http://docs.djangoproject.com/en/1.2/topics/signals/

    【讨论】:

      【解决方案3】:

      您为什么不在另一个方向也添加一个引用,以便每个类别都引用一个子类别列表?然后,您将能够编写两个嵌套循环:一个用于类别的外部循环和一个用于迭代每个类别中的子类别的内部循环。

      【讨论】:

      • 这是正确的想法,但请注意 django 会自动为您执行此操作。它会自动为每个指向模型的外键创建一个“反向关系”,即(如果您没有明确更改它)相关模型的名称加上“_set”。有关详细信息,请参阅以下链接:docs.djangoproject.com/en/dev/topics/db/queries/…
      • 确实django给了你一个API来查询反向关系。然而,使用反向关系来检索相关模型,仍然会导致额外的 SQL 查询,如果多次执行(即在循环中)可能会对性能造成巨大影响。为避免这种情况,可能需要对关系进行非规范化,以便在上述情况下,可以保存对每个类别的子类别的引用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-10
      • 2013-10-02
      • 2013-01-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多