App Based Template Loading in Django

Author profile picture

@pizzapantherPaul Bailey

Father, web developer, and pizza maker; Software Entomologist @saltstack

Sometimes in Django you wish to override a small piece of a template and then refer back to that original template. However, in default Django, once you override that template, you can not refer back to it with the extends tag. This means you have to copy and paste a lot of code you would rather not maintain. So it would be nice if you had an extends tag that worked like:
{% extends "app_name:admin/index.html" %}
I found several snippets for a template loader that does this, but they are quite out of date. So here is a Django 2.2 snippet.
First the loader:
import os

from django.apps import apps
from django.template import Origin, TemplateDoesNotExist
from django.template.loaders.filesystem import Loader as BaseLoader


class Loader(BaseLoader):
    is_usable = True

    def get_template_sources(self, tpl):
        template_parts = tpl.split(":", 1)

        if len(template_parts) != 2:
            raise TemplateDoesNotExist(tpl)

        app_name, template_name = template_parts
        app = apps.get_app_config(app_name)
        template_dir = os.path.abspath(os.path.join(app.path, 'templates'))
        path = os.path.join(template_dir, template_name)

        yield Origin(
            name=path,
            template_name=tpl,
            loader=self,
        )
Then in your settings.py you need to configure your template loaders like:
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'OPTIONS': {
            'loaders': [
                'django.template.loaders.app_directories.Loader',
                'my_app.loader.Loader',
            ],
        },
    },
]
These settings use the default Django template loader first and then use the new App Loader class if nothing is found.
Then you can do something like in a template:
{% extends "grappelli:admin/index.html" %}
{% block custom_views %}
{{ block.super }}
<div class="grp-module" id="app_benchy">
    <h2><a href="/benchmarks/" class="grp-section">Benchmarks</a></h2>
    <div class="grp-row" id="model-benchy">
      <a href="/benchmarks/"><strong>Benchmarks</strong></a>
    </div>
</div>
{% endblock %}
That particular code let me insert a custom block into the Django Grappelli Admin without having to use a dashboard system and without having to copy and paste Django admin code. I use this template loader mostly to override the admin. It's extremely useful here.

Tags

The Noonification banner

Subscribe to get your daily round-up of top tech stories!