142

I want to use the same {% block %} twice in the same django template. I want this block to appear more than once in my base template:

# base.html
<html>
    <head>
        <title>{% block title %}My Cool Website{% endblock %}</title>
    </head>
    <body>
        <h1>{% block title %}My Cool Website{% endblock %}</h1>
    </body>
</html>

And then extend it:

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

# pictures.html
{% extends 'base.html' %}
{% block title %}My Pictures{% endblock %}

# cats.html
{% extends 'base.html' %}
{% block title %}My Cats{% endblock %}

I will get an exception, as Django wants the block to appear only once:

TemplateSyntaxError at /

'block' tag with name 'title' appears more than once

A quick and dirty solution would be duplicating the block title into title1 and title2:

# blog.html
{% extends 'base.html' %}
{% block title1 %}My Blog{% endblock %}
{% block title2 %}My Blog{% endblock %}

But this is a violation of the DRY principle. It would be very difficult as I have a lot of inheriting templates, and also because I don't wanna go to hell ;-)

Is there any trick or work-around to this problem? How can I repeat the same block in my template, without duplicating all the code?

niton
  • 8,771
  • 21
  • 32
  • 52
David Arcos
  • 5,957
  • 5
  • 30
  • 39
  • 2
    also see the solution on this question http://stackoverflow.com/q/1178743/168034 – phunehehe Dec 14 '10 at 07:49
  • 2
    See [this answer particularly](http://stackoverflow.com/a/2460866/565259) to the question phunehehe links to. – Tobu Feb 28 '12 at 14:19

15 Answers15

82

Use the Django template macros plugin:

https://gist.github.com/1715202 (django >= 1.4)

or

http://www.djangosnippets.org/snippets/363/ (django < 1.4)

django >= 1.4

# base.html
{% kwacro title %}
    {% block title %}My Cool Website{% endblock %}
{% endkwacro %}

<html>
    <head>
        <title>{% usekwacro title %}</title>
    </head>
    <body>
        <h1>{% usekwacro title %}</h1>
    </body>
</html>

and

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

django < 1.4

# base.html
{% macro title %}
    {% block title %}My Cool Website{% endblock %}
{% endmacro %}

<html>
    <head>
        <title>{% usemacro title %}</title>
    </head>
    <body>
        <h1>{% usemacro title %}</h1>
    </body>
</html>

and

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}
John R Perry
  • 3,916
  • 2
  • 38
  • 62
80

I think that use of the context processor is in this case an overkill. You can easily do this:

#base.html
<html>
    <head>
        <title>{% block title %}My Cool Website{% endblock %}</title>
    </head>
    <body>
        {% block content %}{% endblock %}
    </body>
</html>

and then:

# blog.html
{% extends 'base.html' %}
{% block content %}
    <h1>{% block title %}My Blog{% endblock %}</h1>
    Lorem ipsum here...
{% endblock %}

and so on... Looks like DRY-compatible.

Anupam
  • 14,950
  • 19
  • 67
  • 94
dqd
  • 1,501
  • 14
  • 10
  • 1
    I might try this tomorrow - I've been wondering how to save a bit of repetition in the templates and this seems like a good approach. thanks. – thebiglife Sep 27 '09 at 19:17
  • 1
    This approach is excellent. I've split out my base.html into base.html and superbase.html, so this also works if you wanted to put a standard title markup (like an h1) in your shared templates. Pages can still override the content of the title block and it will update in both locations. – SystemParadox Dec 16 '11 at 12:00
  • 2
    This doesn't allow to use the text more than twice, does it? – Dennis Golomazov Jan 23 '15 at 09:41
  • 1
    Denis Golomazov: No. In that case, it is better to use the macro plugin (see below). – dqd Jan 27 '15 at 06:44
  • 1
    Can also be applied the other way around: defining the `h1` content inside the block that defines the `title`. Or a block that defines a *part* of the `title`. – Lindlof Mar 07 '16 at 07:45
  • Neat! This approach also lets you define different content, if you need to, in each block. – TheKalpit Apr 04 '18 at 03:48
20

You probably don't actually want to use a block but rather to just use a variable:

# base.html
<html>
    <head>
        <title>{{ title|default:"My Cool Website" }}</title>
    </head>
    <body>
        <h1>{{ title|default:"My Cool Website" }}</h1>
    </body>
</html>

You then set the title through the context.

Aaron Maenpaa
  • 119,832
  • 11
  • 95
  • 108
  • 19
    Probably Dry. But you wouldn't want to set the title within the view; but in the templates. – lprsd Jan 06 '10 at 15:30
  • 8
    Titles should be set from within the templates, not be provided by the context, you need to had a way to define this "title" variable, otherwise this is not a good solution. – Guillaume Esquevin Sep 22 '10 at 14:31
  • That's what the django admin templates do (for {{title}}), but defining the title at a remove is inconvenient. – Tobu Feb 28 '12 at 14:14
17

Here's a way I discovered when trying to do the same thing myself:

# base_helper.html
<html>
    <head>
        <title>{% block _title1 %}{% endblock %}</title>
    </head>
    <body>
        <h1>{% block _title2 %}{% endblock %}</h1>
    </body>
</html>


# base.html
{% extends "base_helper.html" %}

# Copy title into _title1 & _title2, using "My Cool Website" as a default.
{% block _title1 %}{% block _title2 %}{% block title %}My Cool Website{% endblock %}{% endblock %}{% endblock %}

Requires an extra file unfortunately, but doesn't require you to pass the title from the view.

Roman Starkov
  • 59,298
  • 38
  • 251
  • 324
  • In the end I settled for the {% macro %} solution, which doesn't require a new file, and overall lets me express exactly what I want to express. – Roman Starkov Oct 11 '09 at 15:42
  • 1
    simpel and efficient. unlike @dqd s answer, the blocks dont need to be nested, very useful for example for facebook og tags, that might have the same content as other head attributes. upvote! – benzkji May 10 '18 at 09:44
  • 2
    Great answer. This seems even DRYer than @dqd's answer since you have dont have to repeat the

    tag in each template that inherits base. This could be the accepted answer.

    – Anupam Jul 05 '18 at 08:08
  • Excellent! I've used this in more complex scenarios where I wanted to repeat the footer row of a table a the top. And the `` row was rather complex. – caram Dec 06 '19 at 11:36
13

you can use {% include subtemplate.html %} more than once. it's not the same as blocks, but does the trick.

Javier
  • 60,510
  • 8
  • 78
  • 126
  • This has the same problem. The base template won't know which subtemplate to include. – Van Gale Feb 04 '09 at 12:42
  • 1
    Please note that `include` is slower than `block`. https://docs.djangoproject.com/en/1.10/topics/performance/#template-performance – Wtower Dec 13 '16 at 22:07
6

There are some discussion here: http://code.djangoproject.com/ticket/4529 Obviously django core team reject this ticket because they think this is not a common used scenario, however I disagree.

repeat block is simple and clean implementation for this: https://github.com/SmileyChris/django-repeatblock

template macros is another one, however the author mentioned it's not carefully tested: http://www.djangosnippets.org/snippets/363/

I used repeatblock.

Robert Mao
  • 1,911
  • 22
  • 24
  • 5
    The original django-repeatblock repository seems to have been deleted. The best fork of that seems to be https://github.com/phretor/django-repeatblock; I also found https://github.com/ydm/django-sameas, which doesn't require a 'wontfix' Django patch. – natevw Sep 02 '14 at 21:53
5

As an update for anyone coming across this, I've taken the snippet mentioned above and turned it into a template tag library, django-macros, makes the macros more powerful and also implements a repeated block pattern explicitly: django-macros.

Nick
  • 176
  • 1
  • 3
5

Here is a lightweight solution similar to the above do_set and do_get template tag answer. Django allows you to pass the entire template context into a tag which can allow you to define a global variable.

base.html:

<!DOCTYPE html>
<html lang="en">
<head>
  {% block head %}
    <title>{{ title }}</title>
  {% endblock %}
</head>
<body>
  <h1>{{ title }}</h1>
</body>
</html>

page.html:

{% extends "base.html" %}

{% block head %}
  {% define 'title' 'Homepage | title' %}
  {{ block.super }}
{% endblock %}

custom tag (got the idea here: https://stackoverflow.com/a/33564990/2747924):

@register.simple_tag(takes_context=True)
def define(context, key, value):
    context.dicts[0][key] = value
    return ''

Also don't forget to {% load %} your custom tags or add them to the template options builtins list so you don't have to load them in every template. The only limitation to this approach is the {% define %} has to be called from within a block tag because child templates only render block tags that match the parent tags. Not sure if there is a way around that. Also make sure the define call comes before you try to use it obviously.

Community
  • 1
  • 1
manncito
  • 3,814
  • 1
  • 17
  • 16
4

I use this answer to keep things dry.

{% extends "base.html" %}

{% with "Entry Title" as title %}
    {% block title %}{{ title }}{% endblock %}
    {% block h1 %}{{ title }}{% endblock %}
{% endwith %}
Community
  • 1
  • 1
Christian Long
  • 10,385
  • 6
  • 60
  • 58
3

Building on Van Gale's suggestion, you could create get and set tags by adding the following to your templatetags.py file:

register = template.Library()

Stateful = {}
def do_set(parser, token):
    _, key = token.split_contents()
    nodelist = parser.parse(('endset',))
    parser.delete_first_token()  # from the example -- why?
    return SetStatefulNode(key,nodelist)

class SetStatefulNode(template.Node):
    def __init__(self, key, nodes):
        Stateful[key] = nodes
    def render(self, context):
        return ''
register.tag('set', do_set)

def do_get(parser, token):
    tag_name, key = token.split_contents()
    return GetStatefulNode(key)

class GetStatefulNode(template.Node):
    def __init__(self, key):
       self.key = key
    def render(self, context):
        return ''.join( [x.render(context) for x in Stateful[self.key]] )

register.tag('get', do_get)

Then set values in one template via {% set foo %}put data here{% endset %} and get them via {% get foo %} in another.

  • I think that's the most elegant solution of all. Thanks Kieran and Van Gale! – Robert Lacroix Jul 12 '11 at 19:02
  • That's pretty slick, but it seems like it might be even better to render all of the nodes in the Set tag, otherwise they get rendered over and over again by Get. I can think of reasons that might be a good idea (rendering the same stored block inside of different blocks on a page), but I just thought I'd point it out. – acjay Sep 26 '12 at 09:32
3

I too have come across the same need for a repeated {% block %} in my template files. The issue is that I want a Django {% block %} to be used in either case of a Django conditional, and I want the {% block %} to be over-writable by subsequent files that may extend the current file. (So in this case, what I want is definitely more of a block than a variable because I'm not technically re-using it, it just appears on either end of a conditional.

The Problem:

The following Django template code will result in a Template Syntax Error, but I think it's a valid "want" to have a defined {% block %} re-used in a conditional (IE, why is the Django parser validating syntax on BOTH ends of a conditional, shouldn't it only validate the TRUTHY condition?)

# This example shows a {{ DEBUG }} conditional that loads 
#   Uncompressed JavaScript files if TRUE 
#   and loads Asynchronous minified JavaScript files if FALSE.  

# BASE.html
{% if DEBUG %}
    <script src="{{MEDIA_URL}}js/flatfile.1.js"></script>
    <script src="{{MEDIA_URL}}js/flatfile.2.js"></script>
    <script src="{{MEDIA_URL}}js/flatfile.3.js"></script>
    <script type="text/javascript">
        {% block page_js %}
            var page = new $site.Page();
        {% endblock page_js %}
    </script>
{% else %}
    <script type="text/javascript">
        // load in the PRODUCTION VERSION of the site
        // minified and asynchronosly loaded
        yepnope([
            {
                load : '{MEDIA_URL}}js/flatfiles.min.js',
                wait : true,
                complete : function() {
                    {% block page_js %} // NOTE THE PAGE_JS BLOCK
                        var page = new $site.Page();
                    {% endblock page_js %}
                }
            }
        )];
    </script>
{% endif %}

# ABOUT.html
{% extends 'pages/base.html' %}
{% block page_js %}
var page = new $site.Page.About();
{% endblock page_js %}

The Solution:

You can use an {% include %} to conditionally insert a {% block %} more than once. This worked for me because the Django syntax checker includes only the TRUTHY {% include %}. See the result below:

# partials/page.js
{% block page_js %}
    var page = new $site.Page();    
{% endblock %}

# base.html
{% if DEBUG %}
    <script src="{{MEDIA_URL}}js/flatfile.1.js"></script>
    <script src="{{MEDIA_URL}}js/flatfile.2.js"></script>
    <script src="{{MEDIA_URL}}js/flatfile.3.js"></script>
    <script type="text/javascript">
        {% include 'partials/page_js.html' %}
    </script>
{% else %}
    <script type="text/javascript">
        yepnope([
            {
                load : '{MEDIA_URL}}js/flatfiles.min.js',
                wait : true,
                complete : function() {
                    {% include 'partials/page_js.html' %}
                }
            }
        )];
    </script>
{% endif %}
potench
  • 3,802
  • 1
  • 28
  • 39
1

There are two easy solutions for this.

The easiest is to put your title into a context variable. You would set the context variable in your view.

If you are using something like generic views and don't have a views.py for pictures, cats, etc. then you can go the way of a custom template tag that sets a variable in the context.

Going this route would enable you to do something like:

{% extends "base.html" %}
{% load set_page_title %}
{% page_title "My Pictures" %}
...

Then in your base.html:

...
{% block title %}{{ page_title }}{% endblock %}
...
<h1>{{ page_title }}</h1>
Van Gale
  • 43,536
  • 9
  • 71
  • 81
  • However `Any variable set in the context will only be available in the same block of the template in which it was assigned. This behavior is intentional; it provides a scope for variables so that they don’t conflict with context in other blocks.` – Jonathan Jul 10 '14 at 14:46
0

The selected answer alludes to an easy workaround to wrap one tag inside another in the child template to give them both the same value. I use this for social images like so.

Child template:

{% extends 'base.html' %}
...
{% block meta_image %}
{% block meta_image_secure %}
{% if object.cover_pic %}
{{ object.cover_pic.url }}
{% else %}
https://live-static.welovemicro.com/static/img/device-dark.png
{% endif %}
{% endblock %}
{% endblock %}
...

Then in the parent base.html:

...
<meta property="og:image" itemprop="image" content="{% block meta_image %}https://live-static.welovemicro.com/static/img/device-dark.png{% endblock %}">
<meta property="og:image:secure_url" itemprop="image" content="{% block meta_image_secure %}https://live-static.welovemicro.com/static/img/device-dark.png{% endblock %}">
...
Rich Ross
  • 653
  • 6
  • 8
0

I would suggest using the django-macros library.

To install:

pip install django-macros

And then add the 'macros' app in your INSTALLED_APPS list in settings.py.

After that, load the macros custom tags in your base template and use them like that:

{# base.html #}
{% load macros %}

{% macro title %}
    {% block title %} My Cool Website {% endblock %}
{% endmacro %}
    
<html>
    <head>
        <title> {% use_macro title %} </title>
    </head>
    <body>
        <h1> {% use_macro title %} </h1>
    </body>
</html>

-3

In twig you can make this like:

# base.html
<html>
    <head>
        <title>{{ block('title') }}</title>
    </head>
    <body>
        <h1>{{ block('title') }}</h1>
    </body>
</html>

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

# pictures.html
{% extends 'base.html' %}
{% block title %}My Pictures{% endblock %}

# cats.html
{% extends 'base.html' %}
{% block title %}My Cats{% endblock %}
mars
  • 13
  • 2