开发者

Django best practice with foreign key queries

开发者 https://www.devze.com 2023-02-12 04:30 出处:网络
models.py class Category(models.Model): name = models.CharField(max_length=50) class SubCatergory(models.Model):

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

Just wondering if above is best practice for foreign key queries. I'm filtering at the template level (if category.id == sub...), should I 开发者_如何学Gomove this to the model or view level instead?


If there's only ever one depth of subcategory, the following code shouldn't be a problem:

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

But there are some optimization tricks to reduce query count as you'd be doing a query per loop. I'm trying to think of one now.

Actually, I'm starting to think this is an interesting question: best practice?

Your method uses 2 queries. My method is using django practices, but does multiple queries.

To prevent multiple queries, you'd essentially have to do the same thing you've done in your template in your view, namely iterating over SubCatergory, pulling their IDs in python and grouping each ID set onto an attribute on Category.

I don't know the answer to this one.


I think that one good practice here would be to create a template tag for this job. That way you can cache the rendered template and only hit the database at the first render.

First, create the template tag inside your app

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

Then, create the template to be used

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

Override the save method in your models so it will delete the categories list cache entry (forcing it to be rendered at the next request, this could also be placed at the (pre|post)_save signal):

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)

And finally use it like this:

base.html

{% load show_categories_list %}
{% show_categories_list %}

You can also add a timeout to the cache entry, so you don't have to override the save method in your models, but you will have to wait the timeout so it can be rendered again.

Some useful references:

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/


Why don't you add a reference in the other direction as well, so that each Category would reference a list of SubCategories? Then you'll be able to write two nested loops: an outer loop for categories and in inner loop for iterating over the subcategories in each category.

0

精彩评论

暂无评论...
验证码 换一张
取 消