| Version 10 (modified by , 20 years ago) ( diff ) |
|---|
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/