= 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(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: {{{ #!python >>> 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: {{{ #!python >>> 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: {{{ #!python >>> 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 == {{{ #!python 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/