Chapter 7: Template System
7.3 Custom Templates
Custom Filters
Django allows you to create custom filters to extend template functionality. A custom filter is a Python function that takes at most two parameters:
- The value of the variable (input)
- The value of the parameter (optional)
First, create a templatetags directory in your app directory and add an __init__.py file:
myapp/
templatetags/
__init__.py
custom_filters.pyThen define filters in custom_filters.py:
from django import template
register = template.Library()
# Simple filter example
@register.filter
def lower(value):
"""Convert string to lowercase"""
return value.lower()
# Filter with parameters example
@register.filter
def truncate_chars(value, max_length):
"""Truncate string to specified character count"""
if len(value) > max_length:
return value[:max_length] + '...'
return value
# More complex filter example
@register.filter
def word_count(value):
"""Count words in text"""
return len(value.split())
@register.filter
def multiply(value, arg):
"""Multiply value by argument"""
try:
return float(value) * float(arg)
except (ValueError, TypeError):
return 0
@register.filter
def highlight_search(text, search_term):
"""Highlight search keywords"""
if search_term:
return text.replace(
search_term,
f'<mark>{search_term}</mark>'
)
return textUsing custom filters in templates:
{% load custom_filters %}
<p>Title: {{ title|lower }}</p>
<p>Summary: {{ content|truncate_chars:100 }}</p>
<p>Word count: {{ content|word_count }}</p>
<p>Price: {{ price|multiply:1.2|floatformat:2 }}</p>
<p>Search results: {{ content|highlight_search:query }}</p>Custom Tags
Custom tags are more complex than filters and can execute more complex logic. They come in several types:
- Simple tags
- Inclusion tags
- Assignment tags
Define tags in templatetags/custom_tags.py:
from django import template
from django.utils.safestring import mark_safe
from ..models import Article, Category
register = template.Library()
# Simple tag example
@register.simple_tag
def total_articles():
"""Return total number of articles"""
return Article.objects.count()
@register.simple_tag
def article_count_by_category(category_slug):
"""Return number of articles in specified category"""
try:
category = Category.objects.get(slug=category_slug)
return category.articles.count()
except Category.DoesNotExist:
return 0
# Simple tag with parameters
@register.simple_tag(takes_context=True)
def user_article_count(context):
"""Return number of articles by current user"""
user = context['user']
if user.is_authenticated:
return Article.objects.filter(author=user).count()
return 0
# Inclusion tag example
@register.inclusion_tag('tags/recent_articles.html')
def recent_articles(count=5):
"""Display recent articles"""
articles = Article.objects.filter(
status='published'
).order_by('-publish_date')[:count]
return {'articles': articles}
# Assignment tag example
@register.simple_tag
def get_categories():
"""Get all categories"""
return Category.objects.all()
# Complex tag example
@register.tag
def highlight_text(parser, token):
"""
Highlight text tag
Usage: {% highlight_text "search term" %}text to search{% endhighlight_text %}
"""
try:
# Parse tag parameters
tag_name, search_term = token.split_contents()
except ValueError:
raise template.TemplateSyntaxError(
"%r tag requires a single argument" % token.contents.split()[0]
)
# Parse tag content until endhighlight_text
nodelist = parser.parse(('endhighlight_text',))
parser.delete_first_token()
return HighlightTextNode(nodelist, search_term)
class HighlightTextNode(template.Node):
def __init__(self, nodelist, search_term):
self.nodelist = nodelist
self.search_term = template.Variable(search_term)
def render(self, context):
# Render tag content
text = self.nodelist.render(context)
try:
search_term = self.search_term.resolve(context)
# Highlight search term
highlighted = text.replace(
search_term,
f'<mark>{search_term}</mark>'
)
return mark_safe(highlighted)
except template.VariableDoesNotExist:
return textUsing custom tags in templates:
{% load custom_tags %}
<!-- Simple tags -->
<p>Total articles: {% total_articles %}</p>
<p>Python category articles: {% article_count_by_category "python" %}</p>
<p>Your articles: {% user_article_count %}</p>
<!-- Inclusion tags -->
{% recent_articles 10 %}
<!-- Assignment tags -->
{% get_categories as categories %}
<ul>
{% for category in categories %}
<li>{{ category.name }}</li>
{% endfor %}
</ul>
<!-- Complex tags -->
{% highlight_text "Django" %}
Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.
{% endhighlight_text %}Template file for inclusion tag templates/tags/recent_articles.html:
<div class="recent-articles">
<h3>Recent Articles</h3>
<ul>
{% for article in articles %}
<li>
<a href="{{ article.get_absolute_url }}">{{ article.title }}</a>
<span class="date">{{ article.publish_date|date:"m-d" }}</span>
</li>
{% empty %}
<li>No articles available</li>
{% endfor %}
</ul>
</div>Template Context Processors
Template context processors are functions that automatically add variables to all template contexts. They are defined in the TEMPLATES configuration in settings.py.
Create context_processors.py:
from django.conf import settings
def site_info(request):
"""Site information context processor"""
return {
'site_name': getattr(settings, 'SITE_NAME', 'My Website'),
'site_description': getattr(settings, 'SITE_DESCRIPTION', 'A Django website'),
}
def user_permissions(request):
"""User permissions context processor"""
if request.user.is_authenticated:
return {
'user_permissions': request.user.get_all_permissions(),
'is_admin': request.user.is_staff,
}
return {}
def common_data(request):
"""Common data context processor"""
from ..models import Category
try:
categories = Category.objects.all()[:10]
except:
categories = []
return {
'common_categories': categories,
'current_year': datetime.now().year,
}Register context processors in settings.py:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
# Custom context processors
'myapp.context_processors.site_info',
'myapp.context_processors.user_permissions',
'myapp.context_processors.common_data',
],
},
},
]Using variables provided by context processors in any template:
<!-- All templates can access these variables -->
<header>
<h1>{{ site_name }}</h1>
<p>{{ site_description }}</p>
</header>
<nav>
<ul>
{% for category in common_categories %}
<li><a href="{{ category.get_absolute_url }}">{{ category.name }}</a></li>
{% endfor %}
</ul>
</nav>
<footer>
<p>© {{ current_year }} {{ site_name }}</p>
{% if is_admin %}
<p><a href="/admin/">Admin</a></p>
{% endif %}
</footer>Static File Handling
Correctly referencing static files (CSS, JavaScript, images, etc.) in templates:
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>My Website</title>
<!-- CSS files -->
<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'css/responsive.css' %}">
</head>
<body>
<!-- Images -->
<img src="{% static 'images/logo.png' %}" alt="Logo">
<div class="content">
<!-- Page content -->
</div>
<!-- JavaScript files -->
<script src="{% static 'js/jquery.min.js' %}"></script>
<script src="{% static 'js/main.js' %}"></script>
<!-- Static files with version control -->
<script src="{% static 'js/app.js' %}?v=1.0"></script>
</body>
</html>In development environment, configure static files in settings.py:
# settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [
BASE_DIR / "static",
]
STATIC_ROOT = BASE_DIR / "staticfiles"In production environments, it's usually necessary to configure a web server (such as Nginx) to serve static files directly to improve performance.
Summary
Custom template functionality extends the capabilities of the Django template system:
- ✅ Custom filters handle variable formatting
- ✅ Custom tags implement complex logic
- ✅ Template context processors globally inject variables
- ✅ Proper referencing and management of static files
- ✅ Flexible use of inclusion tags and assignment tags
Mastering custom template functionality can create more powerful and flexible frontend interfaces.
Next
We will learn about the Django form system.