| | 1 | = CookBook - Data Model = |
| | 2 | |
| | 3 | == A "category" Data Model == |
| | 4 | |
| | 5 | == Description == |
| | 6 | 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' |
| | 7 | |
| | 8 | == Code == |
| | 9 | {{{ |
| | 10 | #!python |
| | 11 | class Category(models.Model): |
| | 12 | name = models.CharField(core=True, maxlength=200) |
| | 13 | slug = models.SlugField(prepopulate_from=('name',)) |
| | 14 | parent = models.ForeignKey('self', blank=True, null=True, related_name='child') |
| | 15 | description = models.TextField(blank=True,help_text="Optional") |
| | 16 | |
| | 17 | class Admin: |
| | 18 | list_display = ('name', '_parents_repr') |
| | 19 | |
| | 20 | def __str__(self): |
| | 21 | p_list = self._recurse_for_parents(self) |
| | 22 | p_list.append(self.name) |
| | 23 | return self.get_separator().join(p_list) |
| | 24 | |
| | 25 | def get_absolute_url(self): |
| | 26 | if self.parent_id: |
| | 27 | return "/tag/%s/%s/" % (self.parent.slug, self.slug) |
| | 28 | else: |
| | 29 | return "/tag/%s/" % (self.slug) |
| | 30 | |
| | 31 | def _recurse_for_parents(self, cat_obj): |
| | 32 | p_list = [] |
| | 33 | if cat_obj.parent_id: |
| | 34 | p = cat_obj.parent |
| | 35 | p_list.append(p.name) |
| | 36 | more = self._recurse_for_parents(p) |
| | 37 | p_list.extend(more) |
| | 38 | if cat_obj == self and p_list: |
| | 39 | p_list.reverse() |
| | 40 | return p_list |
| | 41 | |
| | 42 | def get_separator(self): |
| | 43 | return ' :: ' |
| | 44 | |
| | 45 | def _parents_repr(self): |
| | 46 | p_list = self._recurse_for_parents(self) |
| | 47 | return self.get_separator().join(p_list) |
| | 48 | _parents_repr.short_description = "Tag parents" |
| | 49 | |
| | 50 | def save(self): |
| | 51 | p_list = self._recurse_for_parents(self) |
| | 52 | if self.name in p_list: |
| | 53 | raise validators.ValidationError("You must not save a category in itself!") |
| | 54 | super(Tag, self).save() |
| | 55 | }}} |
| | 56 | |
| | 57 | == See Also == |
| | 58 | |
| | 59 | http://www.djangoproject.com/documentation/model_api/ |
| | 60 | |
| | 61 | http://www.djangoproject.com/documentation/models/m2o_recursive/ |
| | 62 | |