48

If you want to override a template coming with an app in django (in app/templates/app/) you create a template of the same name in another directory, which the template loader checks before the app's template dir. If you just want to override certain blocks of the template you also have to copy the whole template and change that block, which is actually not very DRY.

Does anybody know a way to override the orginial template, while at the same moment extending it, so that you just have to override the specific block you want to change? (the thing is doing this without changing the template's name, because in some cases you might have to change the view to make it work with another template)

EDIT: As Adam Taylor pointed out in the comments from Django 1.9 on this is possible without any hacks.

umläute
  • 28,885
  • 9
  • 68
  • 122
Bernhard Vallant
  • 49,468
  • 20
  • 120
  • 148
  • @paulo: as far as i know there are of course docs about overriding the app's templates in general, as this is daily practice, but i never saw anything about extending a template that has the same name. If you know it is in the docs please point me to that place... – Bernhard Vallant Oct 19 '10 at 11:38
  • http://docs.djangoproject.com/en/dev/ref/contrib/admin/#overriding-vs-replacing-an-admin-template – Paulo Scardine Oct 19 '10 at 11:53
  • 1
    The approach there only works for app-specific admin templates, as these go to a different path than the original. Iguess that's the reason why this is in the *admin* doc and not in the general one ;) – Florian Ledermann Sep 14 '11 at 16:19
  • 2
    To everybody using Django 1.9+: this is now built into Django. A quote from [the "Templates" section of the Django 1.9 release notes](https://docs.djangoproject.com/en/dev/releases/1.9/#templates): "Django template loaders can now extend templates recursively." – Adam Taylor May 05 '16 at 14:35
  • @AdamTaylor Thanks, that's a very helpful hint to an interesting feature which is buried deep in the release notes. – Bernhard Vallant May 10 '16 at 14:34
  • @AdamTaylor I converted your comment into [an answer](http://stackoverflow.com/a/38894304/247696). – Flimm Aug 11 '16 at 10:50

6 Answers6

37

I think the answer from this related question is pertinent; currently, the best solution seems to be to use a custom template loader from the django-apptemplates package on PyPI, so you can just use pip to install it (e.g. pip install django-apptemplates).

The template loader allows you to extend a template in a specific app; for example, to extend the index page of the admin inteface, you would add

'apptemplates.Loader',

to your TEMPLATE_LOADERS list in settings.py, and use

{% extends "admin:admin/index.html" %}

in your templates.

Community
  • 1
  • 1
leech
  • 8,293
  • 7
  • 62
  • 78
  • 1
    A project I'm on uses this snippet to solve this exact problem. I'm surprised more people don't hit it: an app that wants to tweak parts of existing pages. – Steve Bennett Feb 02 '12 at 00:59
  • 2
    I think this is a common problem, but people make due by duplicating full templates. If anyone has tried, overriding and extending often ends with infinite recursion. The issue is described in this ticket: https://code.djangoproject.com/ticket/15053 – Nagyman Mar 24 '12 at 22:01
10

Django 1.9 and later has this feature:

Django template loaders can now extend templates recursively.

So you can have a file from app1 with contents like this:

app1/templates/example/foobar.html:

<p>I'm from app1</p>
{% block main %}{% endblock %}

And you can extend it with a file from app2 (notice that the template name example/foobar.html is the same):

app2/templates/example/foobar.html:

{% extends "example/foobar.html" %}
{% block main %}
  <p>I'm from app2</p>
{% endblock %}

The end-result should be:

<p>I'm from app1</p>
<p>I'm from app2</p>
Flimm
  • 136,138
  • 45
  • 251
  • 267
  • I've attempted exactly this setup and it doesn't appear to work. – Soul Donut Sep 29 '16 at 15:25
  • @HoHo which Django version are you using? – Flimm Oct 03 '16 at 08:43
  • I'm using 1.10 -- if `app1` has a set of interrelated templates (say `app1/templates/app1/base.html`, which is extended by `app1/templates/app1/subfolder/subsite.html`), how should templates `app2` extend both of these templates? I figured using your method -- where one replicates the folder hierarchy and filenames in `app1` -- would work, but it doesn't appear to. – Soul Donut Oct 03 '16 at 18:42
7

As an update since this seems to be a popular question. I have used the overextends app without any problem. It provides a new keywords overextends that allows to extend templates with the same name.

And it is easy to install with pip:

 pip install -U django-overextends
papirrin
  • 2,004
  • 22
  • 30
2

In the Django wiki, Simon Willison presents a trick to achieve the "self-extending template" effect. It isn't directly applicable if you're using the app_directories template loader, though.

Symlinking apps' templates directories inside a new directory might do a trick.

akaihola
  • 26,309
  • 7
  • 59
  • 69
  • Thanks, thats also not 100% what I was looking for, but I don't think you can get any closer; was already doing something similar when customizing templatesd for different sites! – Bernhard Vallant Oct 27 '10 at 11:13
-1

[update]

I've misread the question, my original answer applies only to the admin app, which has a built-in template extension mechanism. For other app that lacks such mechanism, I would just fork the original templates instead of fiddling with custom template loaders like the chosen answer advises. If you are worried about forking, you can also implement an extension mechanism and contribute back to the original project if you think it is worth.


[original answer]

Straight from the fine manual: Because of the modular design of the admin templates, it is usually neither necessary nor advisable to replace an entire template. It is almost always better to override only the section of the template which you need to change.

To continue the example above, we want to add a new link next to the History tool for the Page model. After looking at change_form.html we determine that we only need to override the object-tools block. Therefore here is our new change_form.html :

{% extends "admin/change_form.html" %}
{% load i18n %}
{% block object-tools %}
{% if change %}{% if not is_popup %}
  <ul class="object-tools">
    <li><a href="history/" class="historylink">{% trans "History" %}</a></li>
    <li><a href="mylink/" class="historylink">My Link</a></li>
    {% if has_absolute_url %}
        <li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">
            {% trans "View on site" %}</a>
        </li>
    {% endif%}
  </ul>
{% endif %}{% endif %}
{% endblock %}

And that's it! If we placed this file in the templates/admin/my_app directory, our link would appear on every model's change form.

Paulo Scardine
  • 73,447
  • 11
  • 124
  • 153
  • Thanks for posting this but my question is not about ADMIN templates. I'm seeking for doing something like this with (frontend)templates that eg. come with a third party app, where I probably just want to change a block, and copying the whole rest to override just a part looks like some DRY violation to me... – Bernhard Vallant Oct 19 '10 at 12:10
  • 1
    @lazerscience: it's the same process, except in your template directory you should adhere to the same directory structure as the templates that you are overriding. Check: http://docs.djangoproject.com/en/dev/ref/templates/api/#django.template.loader.select_template – Andrew Sledge Oct 19 '10 at 14:15
  • 4
    I don't think this still answers the original question. Say that `niceapp.views.index()` uses the template `niceapp/index.html`. If I want to use the original view but customize the template, I can create a `niceapp/index.html` in a `templates` directory of my project or my own app. But how to extend the original and override just some blocks, and not copy the whole template? Saying `{% extend "niceapp/index.html" %}` apparently doesn't do the job. – akaihola Oct 27 '10 at 08:11
  • @akaihola: you are right, I've misread the question. Updated. – Paulo Scardine Mar 09 '13 at 14:59
-3

One simple way to do it is this:

Let's say you want to extend and override django/contrib/admin/templates/admin/change_form.html.

First, copy the original change_form.html to your app's template directory and rename it as something like myapp/templates/admin/original_change_form.html. (You could also do this as a symlink.)

Second, create your own change_form.html in myapp/templates/admin. At the top of it, put the following:

{% extends "admin/original_change_form.html" %}

Simple!

seddonym
  • 16,304
  • 6
  • 66
  • 71