How to repeat a "block" in a django template
DjangoDjango TemplatesDryDjango Problem Overview
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?
Django Solutions
Solution 1 - Django
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 %}
Solution 2 - Django
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.
Solution 3 - Django
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.
Solution 4 - Django
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.
Solution 5 - Django
you can use {% include subtemplate.html %}
more than once. it's not the same as blocks, but does the trick.
Solution 6 - Django
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.
Solution 7 - Django
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.
Solution 8 - Django
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.
Solution 9 - Django
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.
Solution 10 - Django
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 %}
Solution 11 - Django
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 %}
Solution 12 - Django
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>
Solution 13 - Django
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 %}">
...
Solution 14 - Django
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 %}