SyntaxStudy
Sign Up
Django Custom Template Tags and Filters
Django Beginner 1 min read

Custom Template Tags and Filters

Django's built-in template tags and filters cover most common use cases, but you can also write your own. Custom template tags and filters are defined in Python files inside a templatetags directory within an app. The directory must contain an __init__.py file and the Python file must be loaded in the template with {% load mytags %} before the custom tags are available. Simple filters are the easiest to write — they are plain Python functions that take one or two arguments and return a string. You register them with the @register.filter decorator. Custom simple tags are functions decorated with @register.simple_tag that can receive any number of arguments and keyword arguments, making them more flexible than filters. Inclusion tags are the most powerful: they render a sub-template and return it, which is perfect for reusable UI components like a tag cloud or recent posts sidebar. Using {% load static %} and {% load i18n %} are two built-in tag libraries you will use constantly. The {% static %} tag generates the correct URL for a static file (CSS, JS, image) taking the STATIC_URL setting into account. The {% trans %} tag marks strings for translation in multilingual projects. Always use {% static %} instead of hardcoding paths so that your templates work correctly in both development and production.
Example
# blog/templatetags/blog_extras.py
from django import template
from django.utils.html import format_html
from ..models import Post, Tag

register = template.Library()


# --- Simple filter ---
@register.filter(name='reading_time')
def reading_time(text):
    """Estimate reading time in minutes (200 wpm average)."""
    word_count = len(text.split())
    minutes    = max(1, round(word_count / 200))
    return f'{minutes} min read'


# --- Simple tag ---
@register.simple_tag
def total_posts():
    """Return the total number of published posts."""
    return Post.published.count()


# --- Inclusion tag ---
@register.inclusion_tag('blog/partials/latest_posts.html')
def show_latest_posts(count=5):
    """Render the latest N posts as a sidebar widget."""
    latest = Post.published.order_by('-published_at')[:count]
    return {'latest_posts': latest}


# --- Usage in a template ---
# {% load blog_extras %}
#
# <p>{{ post.body|reading_time }}</p>
#
# <p>Total posts: {% total_posts %}</p>
#
# {% show_latest_posts count=3 %}