ChatGPT解决这个技术问题 Extra ChatGPT

How to repeat a "block" in a django template

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?

also see the solution on this question stackoverflow.com/q/1178743/168034
See this answer particularly to the question phunehehe links to.

J
John R Perry

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 %}

This is Fantastic! This can really clean up the problems I gain with sharing templates with django loops and ajax data loops.
Good solution. However, it is "use_macro". "usemacro" is wrong.
Should definitely be built into Django by default.
This solution is very old and kwacros no longer works (I am on Python 3.9, Django 3.2.8)
A
Anupam

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.


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.
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.
This doesn't allow to use the text more than twice, does it?
Denis Golomazov: No. In that case, it is better to use the macro plugin (see below).
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.
A
Aaron Maenpaa

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.


Probably Dry. But you wouldn't want to set the title within the view; but in the templates.
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.
That's what the django admin templates do (for {{title}}), but defining the title at a remove is inconvenient.
R
Roman Starkov

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.


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.
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!
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.

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 <tr> row was rather complex.
J
Javier

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


This has the same problem. The base template won't know which subtemplate to include.
Please note that include is slower than block. docs.djangoproject.com/en/1.10/topics/performance/…
R
Robert Mao

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.


The original django-repeatblock repository seems to have been deleted. The best fork of that seems to be github.com/phretor/django-repeatblock; I also found github.com/ydm/django-sameas, which doesn't require a 'wontfix' Django patch.
N
Nick

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.


Still works in 2021!
Which snippet is this answer referring to?
C
Community

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.


k
kieran hervold

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!
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.
p
potench

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 %}

C
Community

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 %}

V
Van Gale

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>

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.
R
Rich Ross

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 %}">
...

m
mars

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 %}

This is a question about Django.

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now