Django使用外键查询的最佳实践

时间:2011-02-17 14:20:24

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

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 ...),我应该将其移到模型或视图级别吗?

3 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分:2)

我认为这里的一个好习惯是为这份工作创建一个模板标签。这样,您可以缓存渲染的模板,只在第一次渲染时点击数据库。

首先,在应用内创建模板标记

templatetags / 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 %}

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

一些有用的参考资料:

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/

答案 2 :(得分:0)

为什么不在其他方向添加引用,以便每个类别引用SubCategories列表?然后你就可以编写两个嵌套循环:一个用于类别的外循环,在内循环中用于迭代每个类别中的子类别。