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'
Note: the code below is somewhat out of date. meta
should be replaced with models
, for starters...
Code
class Category(meta.Model): category_name = meta.CharField(maxlength=50) parent = meta.ForeignKey('self', blank=True, null=True, related_name='child') 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) class META: admin = meta.Admin( list_display = ('category_name', '_parents_repr'), search_fields = ['category_name'], ) module_name = 'categories'
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): title = meta.CharField('title', maxlength=200), subject = meta.ForeignKey(Category, blank=True, null=True), ...
See Also
http://www.djangoproject.com/documentation/model_api/
http://www.djangoproject.com/documentation/models/m2o_recursive/
http://www.djangoproject.com/documentation/models/m2m_and_m2o/