= CookBook - Data Model = == A "category" Data Model == == Description == I like to have parent/child categories for my documents and weblog entries. I also am a fan of simplicity. This recipe lets you create new categories, and children of categories while providing an intuitively obvious presentation to the user. A parent category of 'Development' with a child category of 'Python' will be displayed as 'Development :: Python'. Using that example, other data models/content types that utilize this code will have drop-downs in the admin interface with entries of 'Development' and 'Development :: Python' == Code == {{{ #!python class Category(models.Model): name = models.CharField(core=True, maxlength=200) slug = models.SlugField(prepopulate_from=('name',)) parent = models.ForeignKey('self', blank=True, null=True, related_name='child') description = models.TextField(blank=True,help_text="Optional") class Admin: list_display = ('name', '_parents_repr') def __str__(self): p_list = self._recurse_for_parents(self) p_list.append(self.name) return self.get_separator().join(p_list) def get_absolute_url(self): if self.parent_id: return "/tag/%s/%s/" % (self.parent.slug, self.slug) else: return "/tag/%s/" % (self.slug) def _recurse_for_parents(self, cat_obj): p_list = [] if cat_obj.parent_id: p = cat_obj.parent p_list.append(p.name) more = self._recurse_for_parents(p) p_list.extend(more) if cat_obj == self and p_list: p_list.reverse() return p_list def get_separator(self): return ' :: ' def _parents_repr(self): p_list = self._recurse_for_parents(self) return self.get_separator().join(p_list) _parents_repr.short_description = "Tag parents" def save(self): p_list = self._recurse_for_parents(self) if self.name in p_list: raise validators.ValidationError("You must not save a category in itself!") super(Tag, self).save() }}} == See Also == http://www.djangoproject.com/documentation/model_api/ http://www.djangoproject.com/documentation/models/m2o_recursive/