views:

1929

answers:

8

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?

+7  A: 

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
Probably Dry. But you wouldn't want to set the title within the view; but in the templates.
Lakshman Prasad
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
A: 

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
+3  A: 

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

Javier
This has the same problem. The base template won't know which subtemplate to include.
Van Gale
I'm accepting this answer, the explanation is in my answer: you can insert the block into the subtemplate.Thanks, Javier!
DZPM
WRONG: The subtemplate can't inherit from the other templates, so this solution is not working. I'm accepting the next answer. Sorry, I should have tested it better :-(
DZPM
A: 

[Edited] This answer proved to be wrong after testing it, because the title.html subtemplate can't inherit from the other templates. Sorry.

I'll try doing the context variables part.

Thanks for your answers

DZPM
This is not an answer to the question.
Carl Meyer
Carl, take a look at the history, I edited the comment because the answer was wrong:http://stackoverflow.com/revisions/511678/list
DZPM
Sorry, -1 removed.
Carl Meyer
+6  A: 

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

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

dqd
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
+2  A: 

Use the Django template macros plugin:

http://www.djangosnippets.org/snippets/363/

Then,

# base.html
{% macro title %}
    {% block title %}My Cool Website{% endblock %}</title>
{% 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 %}
A. Jesse Jiryu Davis
A: 

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.

romkyns
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.
romkyns
A: 

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.

kieran hervold