Skip to content

Custom Template Backends and the Template API

Most of the time, the standard render() shortcut is all you need to return HTML to the user. However, Django gives you direct access to the underlying Python API for advanced use cases. You can even replace Django's default template engine entirely to use a different language, like Jinja2, by writing a custom backend.

The Template API

When you need to render a template outside of a standard web request, such as generating an automated email or creating a PDF, you can interact directly with the Engine, Template, and Context objects.

The Engine is the core system that loads and compiles templates. You can use it to create a template from a raw string instead of loading a file.

python
from django.template import Engine, Context

# Initialize the template engine
engine = Engine()

# Compile a raw string into a Template object
template = engine.from_string("Hello, {{ name }}! Your order is ready.")

# Create a Context object with your data
context = Context({"name": "Alice"})

# Render the final output string
rendered_text = template.render(context)

Creating a Custom Template Backend

Django is designed to support multiple template languages at the same time. If you want to use a template language that Django does not support by default, you must create a custom backend class.

Your custom backend must inherit from django.template.backends.base.BaseEngine and implement a few specific methods. The __init__ method sets up the engine. The from_string method compiles raw text. The get_template method loads a file from the disk.

python
from django.template.backends.base import BaseEngine

class MyCustomEngine(BaseEngine):
    # This defines the subfolder name Django will look for inside installed apps
    app_dirname = "my_templates"

    def __init__(self, params):
        params = params.copy()
        options = params.pop("OPTIONS").copy()
        super().__init__(params)
        # You would initialize your third-party template engine here

    def from_string(self, template_code):
        # Convert a raw string into a custom template object
        return CustomTemplate(template_code)

    def get_template(self, template_name):
        # Load the template code from a file and return the custom template object
        # (File reading logic omitted for brevity)
        return CustomTemplate(template_name)

Both from_string and get_template must return an object that has a render() method. This acts as a wrapper around your third-party template language, making it compatible with Django's standard views.

You can also use Django's built-in lazy evaluation tools to ensure essential security features like CSRF tokens still work with your new engine.

python
from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy

class CustomTemplate:
    def __init__(self, template_code):
        self.template_code = template_code

    def render(self, context=None, request=None):
        if context is None:
            context = {}
        
        if request is not None:
            # Attach CSRF tokens safely if a request object is provided
            context["csrf_input"] = csrf_input_lazy(request)
            context["csrf_token"] = csrf_token_lazy(request)
        
        # Pass the context to your third-party engine and return the final HTML string
        return "Rendered output goes here"

Activating the Custom Template Backend

Once your custom backend class is ready, you must tell Django to use it. You do this by updating the BACKEND path inside the TEMPLATES configuration list in your settings.py file.

python
TEMPLATES = [
    {
        # Point this to the Python path of your custom engine class
        "BACKEND": "my_app.backends.MyCustomEngine",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {},
    },
]