Code: my categories
After some requests I decided to publish my code for categories. It’s very simple. It was inspired by following articles: A "category" Data Model (note: this article uses old-style model format, it doesn’t work anymore) and Relating an object to itself, many-to-one.
from django.core import meta class Category(meta.Model): """ Category defines following fields: name - simple name of category, e.g., 'C++' full_name - full name of category, which includes names of all parents, e.g. 'Development::C++' parent - reference to parent category or null description - HTML description of category, or null Notes: 1) full_name is not editable. It is calculated automatically by calculate_full_name() method during save phase (hook _pre_save). 2) Separator specified by get_separator() method. It can be overridden in subclasses. """ name = meta.CharField(maxlength=50) full_name = meta.CharField(maxlength=250, unique=True, editable=False) parent = meta.ForeignKey('self', blank=True, null=True, related_name='child') description = meta.TextField(blank=True, null=True, help_text="Use raw HTML.") class META: verbose_name_plural = 'Categories' module_name = verbose_name_plural.lower() admin = meta.Admin( list_display = ('full_name',), search_fields = ['full_name',], js = ( '/tiny_mce/tiny_mce.js', '/appmedia/admin/js/textareas.js', ), ) ordering = ('full_name',) def __repr__(self): return self.full_name def _pre_save(self): self.full_name = self.calculate_full_name() def get_separator(self): return '::' # note: used in templates def get_all_children(self): "get list of all children of the category" output =  children = self.get_child_list() for c in children: output.append(c) output.extend(c.get_all_children()) return output # note: the last object of the list is the category itself # note: used in templates def get_all_parents(self): "get list of all parents of the category from top level parent down" id_list =  object = self while True: id_list.append(object) if not object.parent_id: break object = object.get_parent() id_list.reverse() return id_list # note: used in templates def calculate_full_name(self): "calculate full name from parent list" return self.get_separator().join([x.name for x in self.get_all_parents()]) # note: used in templates def get_path(self): "get relative path of the category" return 'categories/%d/' % self.id
As you can see it is very simple. This model defines full_name field, which is auto-populated. I was toying with an idea to keep parent_name and produce full_name by concatenating parent_name with name. But after some trials I decided against it: it introduced a lot of complexities and runtime overhead by saving a few bytes in database. In my opinion it doesn’t worth it. This model defines some convenience methods as well. They are meant to be used in templates.