paint-brush
App Based Template Loading in Djangoby@pizzapanther
821 reads
821 reads

App Based Template Loading in Django

by Paul BaileySeptember 13th, 2019
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Django allows you to override a small piece of a template and then refer back to that original template. This means you have to copy and paste a lot of code you would rather not maintain. Django 2.2 snippets for a template loader that does this are quite out of date. It's extremely useful here. I use this template loader mostly to override the admin. It lets me insert a custom block into the Django Grappelli Admin without having to use a dashboard system and without using Django admin code.
featured image - App Based Template Loading in Django
Paul Bailey HackerNoon profile picture

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.