In this tutorial, we will learn how to implement categories and breadcrumb in a Django site. Categories may be related to each other as child-parent, so categories are hierarchical data and to store and access them efficiently mptt i.e modified preorder tree traversal should be used in which the number of database queries made is minimum. There are some referral links provided at the end of the article to understand how CRUD operations are applied on hierarchical data efficiently.


pip3 install django-mptt

And add mptt in INSTALLED_APPS in

Jump into the code

We will be implementing categories and breadcrumb for a blog website. So consider following models for my_posts app in Django project.

from django.db import models
from mptt.models import MPTTModel, TreeForeignKey

class Post(models.Model):
  title = models.CharField(max_length=120)
  category = TreeForeignKey('Category',null=True,blank=True)
  content = models.TextField('Content')
  slug = models.SlugField()

  def __str__(self):
    return self.title
And category model be :
class Category(MPTTModel):
  name = models.CharField(max_length=50, unique=True)
  parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
  slug = models.SlugField()

  class MPTTMeta:
    order_insertion_by = ['name']

  class Meta:
    unique_together = (('parent', 'slug',))
    verbose_name_plural = 'categories'

  def get_slug_list(self):
      ancestors = self.get_ancestors(include_self=True)
      ancestors = []
      ancestors = [ i.slug for i in ancestors]
    slugs = []
    for i in range(len(ancestors)):
    return slugs

  def __str__(self):

Now run migration commands to create database tables. To show categories in admin panel add following to project_name/my_posts/

from mptt.admin import MPTTModelAdmin,PostAdmin) , MPTTModelAdmin) 

Now run the following command to collect static files from mptt, provided that you’ve already defined STATIC_ROOT in

python3 collectstatic

Doing these steps, you can now add some nested categories by selecting categories > ADD CATEGORY of my_posts app in Django Admin panel, there is an image below to illustrate how to create a category object.

After adding some categories, we have following category tree

If you want to change the indentation pixels i.e the space before each category object so that the relation between them can be determined easily, add the following line of code in

MPTT_ADMIN_LEVEL_INDENT = 20   #you can replace 20 with some other number
                                 #to change indentation space

The category tree showed above is not collapsable and expandable, you can tweak this by changing my_posts/ as

from mptt.admin import DraggableMPTTAdmin,PostAdmin), DraggableMPTTAdmin )

Now go to posts > ADD POST then you will find category drop-down to associate a category to that post, this field is not a required field so you may leave it untouched.

Next, we have to add URL pattern for the category, so go to and add the following line to the urlpatterns list.

url(r'^category/(?P<hierarchy>.+)/$', views.show_category, name='category'),

Now in add the following function.

def show_category(request,hierarchy= None):
    category_slug = hierarchy.split('/')
    parent = None
    root = Category.objects.all()

    for slug in category_slug[:-1]:
        parent = root.get(parent=parent, slug = slug)

        instance = Category.objects.get(parent=parent,slug=category_slug[-1])
        instance = get_object_or_404(Post, slug = category_slug[-1])
        return render(request, "postDetail.html", {'instance':instance})
        return render(request, 'categories.html', {'instance':instance})


Add following code in categories.html.

{% extends 'base.html' %}
{% load static  %}

{% block head_title %} {{ }} {% endblock %}

{% block content %}
<div class="text-center"><h2>{{}}</h2></div>

{% if  instance.children.all %}
    <h4>Sub Categories</h4>
    {% for i in instance.children.all %}
        <a href="{{ i.slug }}"> {{ }} </a><br>
    {% endfor %}

{% endif %}

{% if  instance.post_set.all %}
 {% for i in instance.post_set.all %}
  <div class="row small-up-1 medium-up-3" >

   <div class="column">
    <a href="{{ i.slug }}">
      <div class="card" style="width: 300px; border-color: black">
         <div class="card-divider">
           <strong>{{ i.title | truncatechars:30}}</strong>

         <div class="card-section">
          <small>{{ i.publish}} </small>
          <p>{{ i.content | safe | truncatechars_html:120 }}</p>
{% endfor %}

{% endif %}
{% endblock %}

The CSS framework used is Zurb’s Foundation 6 you may use bootstrap as well.

Next, add following code snippet inside the postDetail.html template to implement breadcrumbs in post detail page ( you should add it above post’s title ).

{% load namify %}

<ul class="breadcrumbs">
    <li><a href="/">Home</a></li>
    {% for i in instance.category.get_slug_list %}
       <li><a href="/category/{{ i }}">{{ i | get_name }}</a></li>
    {% endfor %}


Note that filter get_name is used to extract the name of categories from the slug.

To implement this filter create a directory templatetags at the same level as,, etc in my_post app and create a empty python file to ensure the directory is treated as a Python package and create another python file, add following code in it.

from django import template

register = template.Library()

def get_name(value):
    spam = value.split('/')[-1]         # assume value be /python/web-scrapping
                                        # spam would be 'web-scrapping'
    spam = ' '.join(spam.split('-'))    # now spam would be 'web scrapping'
    return spam

Finally, we have following post detail page for recently created post

The breadcrumb above the title of the post is useful in navigation, for example, if you click web scrapping then it will take you to the catogories.html page where you can navigate posts in web scrapping as well as its sub categories as in the following image.


Modified Preorder Tree Traversal is not just limited to categories but can also be used for efficiently accessing and storing hierarchical data. Although insert and move operations are not as efficient if the tree is changing frequently. However, overall mptt is currently the best solution for database queries overhead. If you have any problem in any step discussed above, you can check our Github Repository from here.

  • django-categories
  • django-treenav

Happy Coding :)