Reputation: 383
Maybe a simple question, but i've just started to work with Django after a few years of php experience :-)
Problem: we have a pair of models - "Categories" and "Post". "Categories" is nested sets tree of post categories, "Post" is plain list of blog posts with ForeignKey field, linked to Categories model. Here is an example:
class Categories(NS_Node):
title = models.CharField(max_length = 150)
slug = models.SlugField(unique = True)
class Post(models.Model):
title = models.CharField(max_length = 150)
slug = models.SlugField(unique = True)
text = models.TextField()
category = models.ForeignKey(Categories)
NS_Node - Class from treebeard library, implements Nested sets data model.
User can see posts for specified category by visiting page with urls like '/books/sci-fi/'. In django view named "category" we need to select only posts that linked to 'sci-fi' category. Now i'm doing it like this:
def category(request, path):
# 'path' is 'books/sci-fi'
# detect current category code from url
category_slug = path.split('/')[-1]
# get current category object
category = Categories.objects.get(slug = category_slug)
# get child categories
childs = category.get_descendants();
# make list of id for needed categories
ids = [child.id for child in childs]
# add current category id
ids.append(category.id)
# selecting posts for current and nested categories
posts_list = Post.objects.filter(category__in = ids)
It works, but inner voice says that there is some unnecessary steps in this code. How to make this code more simple and valid, can you advise? Thanks.
Upvotes: 1
Views: 885
Reputation: 599610
There are a couple of optimisations you could make.
Firstly, the in
filter accepts objects as well as ids, so you could skip the list comprehension:
categories = list(category.get_descendants)
categories.append(category)
Post.objects.filter(category__in=categories)
For a greater improvement, you need to get dirty with the implementation of nested sets. I'm not totally familiar with that, but looking at the treebeard documentation it looks similar to MPTT, which I do understand. If so, we can rely on the fact that the categories we want have a lft
attribute whose value lies between the lft
and the rgt
of the current category to do the whole thing in one go:
Post.objects.filter(category__lft__range=(category.lft, category.rgt))
Upvotes: 6