The view is responsible for the preparation of data for a given template and returning the requested page. What about if we need to generate dynamic content at the main template of our website? In this post I will show you, how to use custom template context processor to dynamically generate site navigation in the master template.

Model

Suppose that we have in the database a list of products and their categories. Somewhere, in the master template we have navigation menu with the static links to the pages, such as: contact, about etc. However, we would like add to the menu, links to the categories and products. Each page of our website will be based on the master template, so you will not have to worry about this.

An examplary model of the category in the products application is as follows:

class Category(models.Model):
name = models.CharField(max_length=50)
description = models.TextField()
slug = models.SlugField(max_length=50, db_index = True)

The field name is used to presents the name of category, description with description of the category. The field slug will contain a normalized name of the category and will be used to combine a link to it. The slug field could be created using the slugify function from the django.template.defaultfilters module.

Model of the Product:

class Product(models.Model):
name = models.CharField(max_length=70)
category = models.ForeignKey(Category)
slug = models.SlugField(max_length=70, db_index = True)

Similarly as the Category model, we create the slug field for the link to the product.

View

In the views.py module in the products application, we create two views which will be responsible for generating a category page which contains a list of products and page of the specified product. The parameter slug will be passed by the URL dispatcher.

def products_list(request, cat_slug):
cat = get_object_or_404(Category, slug__iexact = cat_slug)
prod_list = cat.product_set.order_by('name')
return render(request, 'products.html', {'products' : prod_list, 'cat': cat})

def product(request, product_slug):
prod = get_object_or_404(Product, slug__iexact = product_slug)
return render('product.html', {'product': prod })

ieaxact is a queryset filter to case-insensitive match text.

Template context processor

The next step is to write a custom template context processor, which will pass instances of the Category class to the each template. In the master template we create links to the products based on the slug field. The content of the categories_context.py module is as follows (location of the file is aribtrary):

from products.models import Category

def cats(context):
return {'cats': Category.objects.all()}

In the each template, we have access to the instances of the Category class, through the cats variable. In this example, this function return all categories from database. Of course, you can sort them by name, quantity of their products etc.

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

TEMPLATE_CONTEXT_PROCESSORS = global_settings.TEMPLATE_CONTEXT_PROCESSORS + (
'<APP_NAME>.context_processors.cats',)

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 master template e.g. template_master.html we can use a list of Categories cats for generate links to the specific categories. Sample navigation menu might look like this:

<header>
	<nav>
	<ul>
		<li><a href="/main/">Main page</a></li>
		<li>
			<a href="/products/">Products</a>
			<ul> {% for cat in cats %}
				<li><a href="/products/{{ cat.slug }}">{{ cat.name }}</a></li> {% endfor %}
			</ul>
		</li>
		<li><a href="/contact/">Contact</a></li>
	</ul>
	</nav>
</header>

URL dispatcher

The last step is to add rules related to the routing of the product and category links in the URL dispatcher located in the urls.py module. Only alphanumeric characters and “-” will be accepted for the names of categories and products.

urlpatterns += patterns('',
url(r'^products/([w-]+)?/$', 'products.views.products_list', name='products'),
url(r'^products/[w-]+/([w-]+)/$', 'products.views.product', name='product'))

Summary

Creating a custom template processor context is an easy way to transfer variables globally to all templates. Without the use of this mechanism, you would have in each view pass a list of categories as a template variable and generate a navigation menu individually. In the above example, it has been done once in the master template of the site.


Related posts

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *