Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Creating Custom Template Tags and Filters in Django

Tech May 14 1

Setting Up the Environment

Custom template tags and filters in Django must reside within a specific directory structure inside one of your project's apps. You need to create a new directory named templatetags at the same level as your models.py and views.py files.

Here is an example of the required directory layout for a app named store:

store/
    __init__.py
    models.py
    templatetags/
        __init__.py
        store_extras.py
    views.py

To use your custom tags and filters in a template, you must first load them using the {% load %} tag. For the example above, the template would include:

{% load store_extras %}

For the {% load %} tag to function correctly, the app containing your templatetags directory must be included in the INSTALLED_APPS setting of your project's settings.py file.

Each Python file within the templatetags directory acts as a separate library of tags and filters. You can have multipple files, and the {% load %} tag will import the specific module you name.

Every custom tag or filter library must be registered. This is done by creating a Library instance at the top of your module.

from django import template

register = template.Library()

Writing Custom Template Filters

A custom filter is a Python function that takes one or two arguments:

  • The value of the variable (the input) — this does not have to be a string.
  • An optional argument value.

For example, in the template expression {{ var|my_filter:"arg" }}, the my_filter function will receive the value of var and the string "arg" as its arguments.

Let's create a custom filter named format_price that formats a numeric value as a currency string. This filter will accept an optional currency symbol argument.

def format_price(value, symbol="$"):
    """
    Formats a number as a currency string.
    Example: 1234.56 -> $1,234.56
    """
    try:
        # Ensure the value is a number
        numeric_value = float(value)
        return f"{symbol}{numeric_value:,.2f}"
    except (ValueError, TypeError):
        # Return the original value if it cannot be converted to a float
        return value

Next, let's create another filter, truncate_chars, which truncates a string to a specified number of characters.

def truncate_chars(value, length=50):
    """
    Truncates a string to a specified number of characters.
    If the string is longer than the length, it appends '...'.
    """
    if not isinstance(value, str):
        value = str(value)
    if len(value) > length:
        return value[:length] + "..."
    return value

Registering Custom Filters

There are two primary ways to register a filter with your Library instance.

Method 1: Using the register.filter() Method

You can pass the filter function and its desired name as arguments to the register.filter() method.

register.filter('format_price', format_price)
register.filter('truncate_chars', truncate_chars)

Method 2: Using the register.filter Decorator

This is the more common and concise approach. You can use the register.filter decorator directly above your function definition. You can optionally specify the filter name as an argument to the decorator.

@register.filter(name='format_price')
def format_price(value, symbol="$"):
    # ... function body ...

@register.filter
def truncate_chars(value, length=50):
    # ... function body ...

Comlpete Example

Here is a full, working example demonstrating the creation and use of custom filters.

1. The Custom Filter Module (store/templatetags/store_extras.py)

from django import template

register = template.Library()

@register.filter
def format_price(value, symbol="$"):
    """Formats a number as a currency string."""
    try:
        numeric_value = float(value)
        return f"{symbol}{numeric_value:,.2f}"
    except (ValueError, TypeError):
        return value

@register.filter
def truncate_chars(value, length=50):
    """Truncates a string to a specified number of characters."""
    if not isinstance(value, str):
        value = str(value)
    if len(value) > length:
        return value[:length] + "..."
    return value

2. The View (store/views.py)

from django.shortcuts import render

def filter_demo(request):
    context = {
        'product_price': 199.99,
        'product_description': "This is a very long and detailed description of a fantastic product that we are selling in our online store. It has many features and benefits that you will surely love."
    }
    return render(request, 'store/filter_demo.html', context)

3. The Template (store/templates/store/filter_demo.html)

{% extends "base.html" %}

{% load store_extras %}

{% block content %}
    <h1>Product Details</h1>
    <p><strong>Price:</strong> {{ product_price|format_price }}</p>
    <p><strong>Description:</strong> {{ product_description|truncate_chars:100 }}</p>
{% endblock %}

4. Expected Output

When the template is rendered, you would see the following output:

Product Details
Price: $199.99
Description: This is a very long and detailed description of a fantastic product that we are selling in our online store. It has many features and bene...

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.