Version 10 (modified by aCC, 19 years ago) ( diff )

added error message if category is saved in itself.

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 display 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

class Category(meta.Model):
    verbose_name_plural = 'Categories'
    module_name = verbose_name_plural.lower()
    fields = (
        meta.CharField('category_name', maxlength=50),
        meta.ForeignKey('self', blank=True, null=True,
            rel_name='parent', related_name='child'),
        meta.ForeignKey(User)
    )

    def _recurse_for_parents(self, cat_obj):
        p_list = []
        if cat_obj.parent_id:
            p = cat_obj.get_parent()
            p_list.append(p.category_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 = "Category parents"

    # TODO: Does anybody know a better solution???
    def _pre_save(self):
        p_list = self._recurse_for_parents(self)
        if self.category_name in p_list:
            raise "You must not save a category in itself!"

    def __repr__(self):
        p_list = self._recurse_for_parents(self)
        p_list.append(self.category_name)
        return self.get_separator().join(p_list)

    admin = meta.Admin(
        list_display = ('category_name', '_parents_repr'),
        search_fields = ['category_name'],
    )

API Usage

Let's fill in some root-level categories:

>>> from yourpkg.apps.yourapp.models import yourapp
>>> base_cats = [
...   {'category_name': 'Development'},
...   {'category_name': 'Systems'},
...   {'category_name': 'Management'},
...   {'category_name': 'Community'},
... ]
>>> for cat in base_cats:
...   new_cat = yourapp.Category(**cat)
...   new_cat.save()
>>>

Now, let's create some sub categories, examining the dict as we go:

>>> cat = {'parent_id': 1, 'category_name': 'Python'}
>>> new_cat = yourapp.Category(**cat)
>>> new_cat.__dict__
{'parent_id': 1, 'id': '', 'category_parents': '', 'category_name': 'Python'}
>>> new_cat._pre_save()
>>> new_cat.__dict__
{'_parent_cache': Development, 'parent_id': 1, 'id': '', 'category_parents': 'Development', 'category_name': 'Python'}
>>> new_cat.save()
>>> new_cat.__dict__
{'_parent_cache': Development, 'parent_id': 1, 'id': 5, 'category_parents': 'Development', 'category_name': 'Python'}
>>> new_cat
Development :: Python

Now, let's add a few more and print all the categories:

>>> cat = {'parent_id': 2, 'category_name': 'Administration'}
>>> yourapp.Category(**cat).save()
>>> cat = {'parent_id': 7, 'category_name': 'UNIX'}
>>> yourapp.Category(**cat).save()
>>> for cat in yourapp.categories.get_list():
...   print cat
... 
Community
Development
Management
Systems
Development :: Python
Systems :: Administration
Systems :: Administration :: UNIX

Usage in Other Data Models

class Document(meta.Model):
    fields = (
        meta.CharField('title', maxlength=200),
        meta.ForeignKey(Category, name='subject', blank=True, null=True),
        ...

See Also

http://www.djangoproject.com/documentation/model_api/

http://www.djangoproject.com/documentation/models/m2o_recursive/

Note: See TracWiki for help on using the wiki.
Back to Top