views:

183

answers:

1

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.

+5  A: 

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))
Daniel Roseman
+1 to using the lft and rgt fields which will ultimately give you better performance SQL side
Pras