37

I'm wondering if anyone knows how to deal with the following quirky template structure:

### base.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">

<head>
  <title> {% block title %} Title of the page {% endblock %} </title>
</head>

<body>
  <header>
    {% block header %}
      {% include "base/header.html" %}
    {% endblock header %}
  </header>
  {% block content %}{% endblock %}
</body>

</html>

### base/header.html
<div id="menu-bar">
  {% block nav %}
    {% include "base/nav.html" %}
  {% endblock %}
</div>

### base/nav.html
<nav id="menu">
  <ul>
    <li>
      <a href="/profile/">My Profile</a>
    </li>
    <li>
      <a href="/favs/">My Favorites</a>
    </li>
    {% block extra-content %}{% endblock %}
  </ul>
</nav>

And, the heart of the matter:


### app/somepage.html
{% extends "base.html" %}
{% block content %}
  <p>Content is overridden!</p>
{% endblock %}

{% block extra-content %}
  <p>This will not show up, though...</p>
{% endblock %}

{% block nav %}
  <p>Not even this.</p>
{% endblock %}

The problem is when extending a template you can only override the blocks declared in the parent only, not any of its children.

I suppose I could make base.html a husk of empty unused nested blocks covering all future contingencies, but would even that override properly? And is that the only way?

If you're wondering why I have a bi-directional include/extends workflow around base.html, I have many sub-templates that I want to get used all across the project: Headers, footers, navs, sidebars, etc. They all will be consistant in structure across the entire site, but in many cases a whole subdivision of the site will only need a few of those sub-templates. My idea was to define the sub-templates under the templates/base folder, and have templates/base-type1.html, templates/base-type2.html, etc to extend in other places. Each type would only reference the sub-templates needed, and override them to place content as needed.

Chris Keele
  • 3,364
  • 3
  • 30
  • 52
  • 1
    Well, after posting I see [this question](http://stackoverflow.com/questions/9034331/overwriting-a-block-within-an-included-template-from-an-extended-template) pop up in the sidebar, even after a thorough search of stack overflow and googling. I understand the mechanics of this limitation in django, but man, the implications are depressing. – Chris Keele Apr 03 '12 at 15:32
  • Totally tried to post this as an answer, but my new stack account lacks the rep... Forgot about that. – Chris Keele Apr 03 '12 at 15:33
  • For future finders: the [aforementioned question](http://stackoverflow.com/questions/9034331/overwriting-a-block-within-an-included-template-from-an-extended-template) has a basic code sample a few answers down demonstrating @Marcin's helpful strategy. – Chris Keele Apr 03 '12 at 16:11
  • Seems like my alternative strategy of defining all blocks in advance in base.html won't work either. – Chris Keele Apr 03 '12 at 16:30

3 Answers3

37

It seems to be little known that you can use the with keyword with the include to pass variables into the context of an included template - you can use it to specify includes in the included template:

# base.html
<html>
    <body>
        {% block header %}{% include "header.html" %}{% endblock %}
    </body>
</html>

# header.html
# some stuff here
<div id="header">
    <img src="logo.png">
    {% include nav_tmpl|default:"navigation.html" %}
</div>

# special_page.html (uses other navigation)
{% extends "base.html" %}
{% block header %}
    {% include "header.html" with nav_tmpl="special_nav.html" %}
    # you might also want to wrap the include in an 'if' tag if you don't want anything
    # included here per default 
{% endblock %}

This approach saves you at least from having one additional file just for the purpose of overwriting a block. You can also use the with keyword to pass a value through a bigger hierarchy of includes as well.

Bernhard Vallant
  • 49,468
  • 20
  • 120
  • 148
16

A terser variant to the solution proposed by @Bernhard Vallant:

# base.html
<html>
    <body>
        {% block header %}{% include "header.html" %}{% endblock %}
    </body>
</html>

# header.html
# some stuff here
<div id="header">
    <img src="logo.png">
    {% include nav_tmpl|default:"navigation.html" %}
</div>

# special_page.html (uses other navigation)
{% extends "base.html" %}
{% block header %}
    {% with nav_tmpl="special_nav.html" %}
        {{ block.super }}
    {% endwith %}
{% endblock %}
Community
  • 1
  • 1
6

You can solve this by extending your currently-included templates, then including the extension instead of the the currently-included base template.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • 2
    Your solution would work if I only had a linear hierarchy of templates, as in the bare-bones example above. IRL my situation is a branching hierarchy with base.html as the trunk of the tree. As briefly mentioned, it also includes a footer, a sidebar, etc; meaning I can't simply flip the direction of inclusion and extend the header. – Chris Keele Apr 03 '12 at 15:40
  • @ChrisKeele As long as your include is in a block, you can replace the include with the inclusion of a derived template, so yes this would work. – Marcin Apr 03 '12 at 15:45
  • 1
    So if I had a header.html and a footer.html, I'd make each extend base.html, then in example.html... Do what? Including the header and footers? And extending base on top of that? Sorry for needing further clarification, but I'm away from code right now, and "replace the include with the inclusion of a derived template" is hard for me to imagine without a sandbox. :) – Chris Keele Apr 03 '12 at 15:51
  • @ChrisKeele No, if you want to create example.html, extend base.html, and override the blocks that import the default header.html with your example_header.html, which itself derives from the default header.html. – Marcin Apr 03 '12 at 15:53
  • Bring in a fourth player, gotcha. That makes perfect sense, and is about as ugly. I think I'd rather have one inelegant husk of a base.html and sub-templates in an out-of the way directory than sully my app's template directories with a bunch of template stubs... I wish there were a middle ground. Which would you prefer, context of my project aside? – Chris Keele Apr 03 '12 at 16:01
  • @ChrisKeele I honestly think it depends on how the templates relate to each other. I think the approach I suggest is more general, but is kind of fragile. Too many different blocks in your base template, and it could get chaotic. Sometimes there's no good solution. – Marcin Apr 03 '12 at 16:05
  • In my case, I'm leveraging this template structure mostly for a highly-styled, reusable but customizable framework around the actual core content of my project... Which means quite a few blocks in the base and quite a few stubs. But I like this trick, I'll keep it in mind next time I'm doing a project I get to style myself. Thanks! – Chris Keele Apr 03 '12 at 16:08
  • 1
    Doh.. This is one of those lessons that seems so simple after you learn it, but so impossibly complex beforehand. Thank You! – Matthew Weber Jul 27 '18 at 19:53