Django: Override block that was included in parent template

EDIT: The great Bernhard Vallant has a much more elegant solution to the problem: http://stackoverflow.com/questions/9996428/django-templates-overriding-blocks-of-included-children-templates-through-an-ex/20220005#20220005

This renders everything below obsolete :)


Let's imagine the following setup for your projects templates. You have a base template where all the barebone html of your project lives. It contains the basic building blocks of your website like header, footer, content etc.

You have a product template that extends the base template. In the product template you just have to override the content block of the base template and you have a nice product page.

Because your header, with all this navigation, subnavigation, language switching menu, user menu, ads and so on in it, is getting more and more complex, you extract it in its own base_header template.

In code this looks something like this:

=== base_header.html ===
<nav class="primary">  
    {% block nav %}{% endblock nav %}
</nav>  
<nav class="primary">  
    {% block subnav %}{% endblock subnav %}
</nav>


=== templates/base.html ===
...
<body>  
  <header>
    {% include “base_header.html” %}
  </header>
  <section class="main">
    {% block content %}
    {% endblock content %}
  </section>
</body>  
...


=== product.html ===
{% extends “base.html” %}
{% block content %}
    <!-- your html for the product -->
{% endblock content %}

Now lets assume you have a special product X. And because it is so special it gets its own template product_x. And you would also like to give product X a custom header...

But how do you override a block that was included in the parent template?

Django does not support this out of the box, but there is a nifty little pattern you can use to acomplish this. You have to do two things:

  1. In your base template add a block (let's call it header_include) around the include statement of base_header.html
  2. In your product x template override the header_include and include your custom header.

In code this looks something like this:

=== base_header.html ===
<nav class="primary">  
    {% block nav %}{% endblock nav %}
</nav>  
<nav class="primary">  
    {% block subnav %}{% endblock subnav %}
</nav>


=== templates/base.html ===
...
<body>  
  <header>
    {% block header_include %}
      {% include “base_header.html” %}
    {% endblock header_include %}
  </header>
  <section class="main">
    {% block content %}
    {% endblock content %}
  </section>
</body>  
...


=== product_x.html ===
{% extends “base.html” %}
{% block header_include %}
    {% include "product_x_header.html" %}
{% endblock header_include %}
{% block content %}
    <!-- your html for the product -->
{% endblock content %}


=== product_x_header.html ===
{% extends "base_header.html" %}
{% block subnav %}
   <!-- your custom header html -->
{% endblock subnav %}

That is it. If you keep your blocks and template files named in a consistent way, this really a good way for organizing your templates in Django.

Of course this little trick was not invented by me. As so often in the past I found the solution for this problem on Stack Overflow. It was an answer by the User Marcin to the question Django templates: overriding blocks of included children templates through an extended template. Thank's a lot Marcin, really a nifty pattern!