How to build a dynamic menu in Django

  • By PyCat
  • Jan 12, 2017
  • Django Practices

[Photo by Pankaj Patel on Unsplash]

A Django view is responsible for quering the data for a given template and returning the requested HTML. What about if we need to generate dynamic content at the base template of our website? This post demonstrates , how to use a custom template context processor to dynamically generate site navigation menu in the base Django template.

The goal is to buld a custom navigation menu by rendering a list of custom pages ans setting also "active" class to the selected menu. We will follow the Django MVC pattern using also a custom context processor for the pages application.

Database Model

Consider that we have in the database a list of custom pages. We also have in our base template our navigation menu with static links, such us contact, about etch. Our goal is to render this links dynamicaly. Remenber that each page of our site extends the base.html template

An examplary model of the application is as follows:

class Page(models.Model):
        ('draft', 'Draft'),
        ('published', 'Published'),
    menu_title = models.CharField(max_length=200)
    page_title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200,unique_for_date='publish')
    body = RichTextField()
    show_in_menu = models.BooleanField(default=False)
    publish = models.DateTimeField( auto_now=True)
    status = models.CharField(max_length=10,choices=STATUS_CHOICES,default='draft')
    image = models.ImageField(upload_to='page', blank=True, null=True)

    def get_absolute_url(self):
        return reverse('page_detail', args=[self.slug])


I assume that model fields are know so I wont proceed to further explanation. I you have questions about model fields you could check on Django Model Field Reference


In the of the page app we create a view witch will be responsible for quering the data o a specified page and returning the related HTML template. 

from .models import Page

def page_detail(request, slug):
    page = get_object_or_404(Page, slug=slug)
    return render(request, 'page/page_detail.html', {'page': page})


Custom template context processor

Our custom template context processor will pass instances of the Page class to the each template. In the base template we create links to the pages based on the slug field. The module for our custom template context processor will be placed in a file called, as follows (location of the file is aribtrary):


from .models import Page

def show_pages_menu(context):
    pages_menu = Page.objects.filter(show_in_menu=True)
    return {'pages_menu': pages_menu}

Using the custom context template processor means that in each template we have access to the Page class though the pages variable. In the example above the function retrieves and returns all the Pages from the database. Of course you can short them as you prefer

The next step is register a template context processor. In the Django configuration module you have to set full path to the function:

        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR,'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [

You don’t override the existing settings. Append a path to the custom template context processor at the end of list of tuples.

Navigation menu

Now, in the base template e.g. we can use a list of Pages pages_menu for generate links to the specific pages. Sample navigation menu code might look like this:

Define the URL's

 The next step is to add rules related to the routing of the page links in the Django URL dispatcher located in the django urls file. In our case the urls should look like this:

from django.conf.urls import include, url
from django.conf import settings
from django.contrib import admin

from . import views

urlpatterns =[
       url(r'^post/(?P[\w-]+)/$', views.post_detail, name='post_detail'),
       url(r'^$', views.post_list, name='post_list'),
       url(r'^tag/(?P[\w-]+)/$', views.post_list, name='post_list_by_tag'),
       url(r'^search/$', views.post_search, name='post_search'),

Load in template

And the final step is to load/render the specific menu data in your template. Let's say we have a menu.html to hold the menu markup. Our template coud look like this:


{# First render the pages #}
{% for page in pages_menu %}

{# Then check if current page is 'published' #}
{% if page.status == 'published' %}

  <a href="{{ page.get_absolute_url }}">{{page.menu_title}}</a>

{% endif %}
{% endfor %}



The custom template context processor is an easy way to transfer variables globally accesible from all the templates. Without the use of this infrastructure we would had have to pass in each view a list of pages as a template variable - witch means to repeat code - and generate a new menu individually witch is not a quite good practice. The above menu implemetation its just one example on how to make use of the custom template context processor. There are lots of "problems" that could be solved using this practice, such as rendering data for a sidebar section etc.

Enjoy your new custom navigation menu!

Find me