Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django templates: overriding blocks of included children templates through an extended template

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.

like image 282
Chris Keele Avatar asked Apr 03 '12 15:04

Chris Keele


2 Answers

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.

like image 114
Bernhard Vallant Avatar answered Oct 07 '22 15:10

Bernhard Vallant


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 %} 
like image 39
Julián Landerreche Avatar answered Oct 07 '22 14:10

Julián Landerreche