| 1 | = CookBook - Data Modela = |
| 2 | |
| 3 | == Description == |
| 4 | 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 entires of 'Development' and 'Development :: Python' |
| 5 | |
| 6 | == Code == |
| 7 | {{{ |
| 8 | class Category(meta.Model): |
| 9 | verbose_name_plural = 'Categories' |
| 10 | module_name = verbose_name_plural.lower() |
| 11 | fields = ( |
| 12 | meta.CharField('category_name', maxlength=50), |
| 13 | meta.CharField('category_parents', maxlength=250, editable=False), |
| 14 | meta.ForeignKey('self', blank=True, null=True, |
| 15 | rel_name='parent', related_name='child'), |
| 16 | ) |
| 17 | unique_together = (("category_name", "category_parents"),) |
| 18 | |
| 19 | def _recurse_for_parents(self, cat_obj): |
| 20 | p_list = [] |
| 21 | if cat_obj.parent_id: |
| 22 | p = cat_obj.get_parent() |
| 23 | p_list.append(p.category_name) |
| 24 | more = self._recurse_for_parents(p) |
| 25 | p_list.extend(more) |
| 26 | if p_list: |
| 27 | p_list.reverse() |
| 28 | return p_list |
| 29 | |
| 30 | def get_separator(self): |
| 31 | return ' :: ' |
| 32 | |
| 33 | def _parents_repr(self): |
| 34 | p_list = self._recurse_for_parents(self) |
| 35 | return self.get_separator().join(p_list) |
| 36 | |
| 37 | def __repr__(self): |
| 38 | if self.category_parents: |
| 39 | l = [self.category_parents, self.category_name] |
| 40 | return self.get_separator().join(l) |
| 41 | return self.category_name |
| 42 | |
| 43 | def _pre_save(self): |
| 44 | self.category_parents = self._parents_repr() |
| 45 | |
| 46 | admin = meta.Admin( |
| 47 | list_display = ('category_name', 'category_parents'), |
| 48 | search_fields = ['category_name', 'category_parents'], |
| 49 | ) |
| 50 | ordering = ('category_parents', 'category_name') |
| 51 | }}} |
| 52 | |
| 53 | == API Usage == |
| 54 | Let's fill in some root-level categories: |
| 55 | {{{ |
| 56 | >>> from yourpkg.apps.yourapp.models import yourapp |
| 57 | >>> base_cats = [ |
| 58 | ... {'category_name': 'Development'}, |
| 59 | ... {'category_name': 'Systems'}, |
| 60 | ... {'category_name': 'Management'}, |
| 61 | ... {'category_name': 'Community'}, |
| 62 | ... ] |
| 63 | >>> for cat in base_cats: |
| 64 | ... new_cat = yourapp.Category(**cat) |
| 65 | ... new_cat.save() |
| 66 | >>> |
| 67 | }}} |
| 68 | |
| 69 | Now, let's create some sub categories, examining the dict as we go: |
| 70 | {{{ |
| 71 | >>> cat = {'parent_id': 1, 'category_name': 'Python'} |
| 72 | >>> new_cat = yourapp.Category(**cat) |
| 73 | >>> new_cat.__dict__ |
| 74 | {'parent_id': 1, 'id': '', 'category_parents': '', 'category_name': 'Python'} |
| 75 | >>> new_cat._pre_save() |
| 76 | >>> new_cat.__dict__ |
| 77 | {'_parent_cache': Development, 'parent_id': 1, 'id': '', 'category_parents': 'Development', 'category_name': 'Python'} |
| 78 | >>> new_cat.save() |
| 79 | >>> new_cat.__dict__ |
| 80 | {'_parent_cache': Development, 'parent_id': 1, 'id': 5, 'category_parents': 'Development', 'category_name': 'Python'} |
| 81 | >>> new_cat |
| 82 | Development :: Python |
| 83 | }}} |
| 84 | |
| 85 | |
| 86 | Now, let's add a few more and print all the categories: |
| 87 | {{{ |
| 88 | >>> cat = {'parent_id': 2, 'category_name': 'Administration'} |
| 89 | >>> yourapp.Category(**cat).save() |
| 90 | >>> cat = {'parent_id': 7, 'category_name': 'UNIX'} |
| 91 | >>> yourapp.Category(**cat).save() |
| 92 | >>> for cat in yourapp.categories.get_list(): |
| 93 | ... print cat |
| 94 | ... |
| 95 | Community |
| 96 | Development |
| 97 | Management |
| 98 | Systems |
| 99 | Development :: Python |
| 100 | Systems :: Administration |
| 101 | Systems :: Administration :: UNIX |
| 102 | }}} |
| 103 | == Usage in Other Data Models == |
| 104 | {{{ |
| 105 | class Document(meta.Model): |
| 106 | fields = ( |
| 107 | meta.CharField('title', maxlength=200), |
| 108 | meta.ForeignKey(Category, name='subject', blank=True, null=True), |
| 109 | ... |
| 110 | }}} |