Creating Custom Categories#

Django Categories isn’t just for using a single category model. It allows you to create your own custom category-like models with as little or much customization as you need.

Name only#

For many cases, you want a simple user-managed lookup table. You can do this with just a little bit of code. The resulting model will include name, slug and active fields and a hierarchical admin.

  1. Create a model that subclasses CategoryBase

     1from categories.models import CategoryBase
     2
     3
     4class SimpleCategory(CategoryBase):
     5    """
     6    A simple of catgorizing example
     7    """
     8
     9    class Meta:
    10        verbose_name_plural = "simple categories"
    
  2. Create a subclass of CategoryBaseAdmin.

     1from django.contrib import admin
     2
     3from categories.admin import CategoryBaseAdmin
     4
     5from .models import SimpleCategory
     6
     7
     8class SimpleCategoryAdmin(CategoryBaseAdmin):
     9    pass
    10
    11
    12admin.site.register(SimpleCategory, SimpleCategoryAdmin)
    
  3. Register your model and custom model admin class.

Name and other data#

Sometimes you need more functionality, such as extra metadata and custom functions. The Category model in this package does this.

  1. Create a model that subclasses CategoryBase as above.

  2. Add new fields to the model. The Category model adds these extra fields.

     1from categories import models, settings
     2from categories.base import CategoryBase
     3
     4
     5class Category(CategoryBase):
     6    thumbnail = models.FileField(
     7        upload_to=settings.THUMBNAIL_UPLOAD_PATH,
     8        null=True,
     9        blank=True,
    10        storage=settings.THUMBNAIL_STORAGE,
    11    )
    12    thumbnail_width = models.IntegerField(blank=True, null=True)
    13    thumbnail_height = models.IntegerField(blank=True, null=True)
    14    order = models.IntegerField(default=0)
    15    alternate_title = models.CharField(
    16        blank=True, default="", max_length=100, help_text="An alternative title to use on pages with this category."
    17    )
    18    alternate_url = models.CharField(
    19        blank=True,
    20        max_length=200,
    21        help_text="An alternative URL to use instead of the one derived from " "the category hierarchy.",
    22    )
    23    description = models.TextField(blank=True, null=True)
    24    meta_keywords = models.CharField(
    25        blank=True, default="", max_length=255, help_text="Comma-separated keywords for search engines."
    26    )
    27    meta_extra = models.TextField(
    28        blank=True, default="", help_text="(Advanced) Any additional HTML to be placed verbatim " "in the <head>"
    29    )
    
  3. Add new methods to the model. For example, the Category model adds several new methods, including overriding the save() method.

     1from categories.models import Category
     2
     3
     4def save(self, *args, **kwargs):
     5    if self.thumbnail:
     6        import django
     7        from django.core.files.images import get_image_dimensions
     8
     9        if django.VERSION[1] < 2:
    10            width, height = get_image_dimensions(self.thumbnail.file)
    11        else:
    12            width, height = get_image_dimensions(self.thumbnail.file, close=True)
    13    else:
    14        width, height = None, None
    15
    16    self.thumbnail_width = width
    17    self.thumbnail_height = height
    18
    19    super(Category, self).save(*args, **kwargs)
    
  4. Alter Meta or MPTTMeta class. Either of these inner classes can be overridden, however your Meta class should inherit CategoryBase.Meta. Options for Meta are in the Django-MPTT docs.

    1from categories.base import CategoryBase
    2
    3
    4class Meta(CategoryBase.Meta):
    5    verbose_name_plural = "categories"
    6
    7
    8class MPTTMeta:
    9    order_insertion_by = ("order", "name")
    
  5. For the admin, you must create a form that subclasses CategoryBaseAdminForm and at least sets the Meta.model attribute. You can also alter the form fields and cleaning methods, as Category does.

     1from categories.base import CategoryBaseAdminForm
     2from categories.models import Category
     3
     4
     5class CategoryAdminForm(CategoryBaseAdminForm):
     6    class Meta:
     7        model = Category
     8
     9    def clean_alternate_title(self):
    10        if self.instance is None or not self.cleaned_data["alternate_title"]:
    11            return self.cleaned_data["name"]
    12        else:
    13            return self.cleaned_data["alternate_title"]
    
  6. Next you must subclass CategoryBaseAdmin and assign the form attribute the form class created above. You can alter any other attributes as necessary.

     1from categories.admin import CategoryAdminForm
     2from categories.base import CategoryBaseAdmin
     3
     4
     5class CategoryAdmin(CategoryBaseAdmin):
     6    form = CategoryAdminForm
     7    list_display = ("name", "alternate_title", "active")
     8    fieldsets = (
     9        (None, {"fields": ("parent", "name", "thumbnail", "active")}),
    10        (
    11            "Meta Data",
    12            {
    13                "fields": ("alternate_title", "alternate_url", "description", "meta_keywords", "meta_extra"),
    14                "classes": ("collapse",),
    15            },
    16        ),
    17        (
    18            "Advanced",
    19            {
    20                "fields": ("order", "slug"),
    21                "classes": ("collapse",),
    22            },
    23        ),
    24    )