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

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>
My Profile
</li>
<li>
My Favorites
</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.

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.

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

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

Related

Passing a variable to include in extends in Django templates

I have the following structure of templates:
main.html
<html>
<body>
<p>
This works: {% block title %}{% endblock %}
</p>
{% include 'heading.html' with title=title %} {# but this does not work since it is not a variable #}
</body>
</html>
heading.html
<p>
{{ title }}
</p>
page.html
{% extends 'main.html' %}
{% block title %}test title{% endblock %}
How can I pass the title from page.html to heading.html? Ideally, it should be defined as a block like now, but alternatives are also welcome. I'd like to contain the solution within the templates if possible.
Clarification:
This is a minimal example, but I have multiple pages like main.html that share a larger header that has title and some other variables that I'd like defined in the child template (not necessarily as variable as long as the text is passed).
I could put the title into the view code, but this solution would just decrease the separation of displayed data from the logic.
Use a block around the include with variables, and then wrap {{ block.super }} in the with template tag.
main.html:
<html>
<body>
<p>
This works: {% block title %}{% endblock %}
</p>
{% block with_variables %}
{% include 'heading.html' %}
{% endblock %}
</body>
</html>
page.html:
{% extends 'main.html' %}
{% block title %}test title{% endblock %}
{% block with_variables %}
{% with title="variable title" %}
{{ block.super }}
{% endwith %}
{% endblock %}
You may name it {% block heading %}, or nest the whole main.html in {% block content %}.
instead of {% include %}, you might want to use custom template tags with returning html or extension like slippers
You don't need heading.html. You can use the context variable title directly; as long as you pass it to your render function in your view, e.g.:
views.py
def my_view(request):
context = {'title': 'Hello World!'}
return render(request, 'page.html', context=context)
main.html
<html>
<body>
<p>
This is my title:
{% block title %}{% endblock %}
</p>
</body>
</html>
page.html
{% extends 'main.html' %}
{% block title %}
<strong>{{title}}</strong>
{% endblock %}
If you have administrative rights to your Django installation, an easy and elegant solution would be to switch your Django's template renderer to Jinja2 instead using django-jinja as a backend.
The main advantage of Jinja2 for the needs of this question is that it supports assignment of a block to a variable through block assignments. Since Jinja2 is inspired by the Django template language, your existing templates will need few modifications, if any.
With Jinja2 as a template renderer, you can set the title variable with a block of HTML content in the child template:
page.html
{% extends 'main.html' %}
{% set title %}test title{% endset %}
The title variable will then work in the shared template:
main.html
<html>
<body>
<p>
Main template works: {{ title }}
</p>
{% include 'heading.html' %}
</body>
</html>
And it will also work in the included template:
heading.html
<p>
Included template also works: {{ title }}
</p>
Demo: https://replit.com/#blhsing/AlarmedCleverDebugmonitor
One possible solution is to split heading.html into two and add a {% block %} in-between, but this is an ugly workaround that is prone to bugs with unclosed tags.
I am searching for a better alternative.
main.html
...
{% include 'heading1.html' %} {# containing <p> #}
{% block title %}{% endblock %}
{% include 'heading2.html' %} {# containing </p> #}
...
Additionally, if white spaces are important, there has to be no new line between includes and the block.
add your title to the rendering context and it will be visible in page, main and included heading.

instead of {% block content %} I'm using {% block sub-header%} under navbar, isnt this valid?

Hello I want to have different sub header under navbar for different pages. so I used {% block %} I learnt. but this time it's not showing anything, no content no error. am I doing it wrong?
Inside navbar.html
<nav>
<div id="bottom header" style="background:yellow;">
{% block sub-header %}
{% endblock %}
</div>
</nav>
then inside base.html I have
extends navbar.html
then inside index.html I have
extends base.html
{% block sub-header %}
<p>hello yall</p>
{% endblock %}
Django does not process blocks in included files.
The include tag should be considered as an implementation of "render
this subtemplate and include the HTML", not as "parse this subtemplate
and include its contents as if it were part of the parent". This means
that there is no shared state between included templates -- each
include is a completely independent rendering process.
See the documentation for more information.
The problem is that {% block sub_header %} is inside navbar.html, which is an included template on base.html. One way to get around this restriction is to extract parts of your nav into the base:
<!-- base.html -->
<body>
<nav class="navbar navbar-default navbar-static-top" role="navigation">
{% include 'navbar.html' %}
{% block sub_header %}{% endblock %}
</nav>
<!-- other HTML here -->
</body>
Then your templates which extend base.html can use {%block sub_header %}
<!-- my-template.html -->
{% extends 'base.html' %}
{% block sub_header %}
<h1>Hello World!</h1>
{% endblock %}

is dividing a template into parts and including each part bad?

I have a base template that I'd like to split up into three parts: header, body, footer. Then I use the base template to include the three sub-templates. However, from what I've seen, this means I cannot override {{ block }} content. Is using includes a bad idea then? Or is there a way to override block content in an included template?
I know that you can send static context variables to the included segment, but it needs to be more dynamic.
My code:
In header.html
<html>
<head>
<script url="..."></script>
<link rel="..." />
{% block head_extension %}
{% endblock %}
</head>
<body>
<header>
<div class="headerstuff">
</div>
</header>
Then in the body.html file:
<div class="container">
{% block content %}
Foo fum my content
{% endblock %}
</div>
footer.html:
<footer>
{% block footer %}
Copyright 2015
{% endblock %}
</footer>
</body>
</html>
base.html:
{% include "header.html" %}
{% include "body.html" %}
{% include "footer.html" %}
<!-- and the part that doesn't work -->
{% block head_extension %}
<script src="unique_script"></script>
{% endblock %}
{% block content %}
My unique content
{% endblock %}
{% block footer %}
Copyright 2011
{% endblock %}
<!-- end broken django templating try -->
Am I doing something wrong? The templating documentation seemed to indicate that what I am trying to do doesn't work. It seems like this would be the best way to create easy-to-read templates. Is it better just to have all parts in one large file? As you can imagine, the header, body, and footer elements are much larger than this demonstration. But the point remains.
I am hoping there is a way to do what I'm thinking of that I'm not aware of.
Thanks in advance
Your approach is fine but you are doing this in wrong order. First of all the html starting <html> and ending tag </html> should not be split into different files, it is good to have it in base.html.
Below is an example of how to follow the breakup structure:
base.html
<html>
<head>
<!-- Some stuff here which should be included in all templates for example js or css -->
{% block extra_css %}
<!-- to included app-template dependent css -->
{% endblock extra_css %}
{% block extra_js %}
<!-- to included app-template dependent js -->
{% endblock extra_js %}
{% block extra_head %}
<!-- for anything else inside head -->
{% endblock extra_head %}
</head>
<body>
{% block menu %}
<!-- Default menu here -->
{% block extra_menu %}
<!-- extend menu based on template -->
{% endblock extra_menu %}
{% endblock menu %}
{% block content %}
<div>This is good</div>
{% endblock content %}
{% include "footer.html" %}
{% block bottom_js %}
<!-- if you want to have manual js scripts at bottom -->
{% endblock bottom_js %}
</body>
</html>
Now base.html is all setup now lets suppose from another child template you want to override the base.html block content you will do:
child.html
{% extends "base.html" %}
{% block content %}
<div>This is really good</div>
{% endblock content %}
So when the page will be loaded you will see this is really good instead of this is good (which was defined in base.html inside content block) because you just override it.
If you want that whatever inside the content block in base.html should also be preserved then you need to extend the block rather than overriding it completely by using method {{ block.super }}
child.html
{% extends "base.html" %}
{% block content %}
{{ block.super }}
<div>This is really good</div>
{% endblock content %}
Now you will see both this is good and this is really good. Hope this will clarify your concept and will lead you good.

How to chain {% includes %} in django templating

I have a base.html file that looks like this:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
{% block header %}{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
{% block footer %}{% endblock %}
</body>
</html>
and I have a file, auth.html that extends this:
{% extends "base.html" %}
{% block content %}
[MY CONTENT]
{% endblock %}
which works fine, but I also want to have a separate header.html file that plugs into the header block above.
What's the correct way to structure auth.html and header.html in order to include both and to have both extend base.html?
I tried adding a {% include header.html %} line to auth.html, and structuring header.html as follows:
{% extends "base.html" %}
{% block header %}
[HEADER CONTENT HERE]
{% endblock %}
but that didn't work. How should I be doing this?
You need {{ block.super }}:
If you need to get the content of the block from the parent template,
the {{ block.super }} variable will do the trick. This is useful if
you want to add to the contents of a parent block instead of
completely overriding it.
Its burried in the template inheritance documentation.
Suppose you want to add extra stuff to the header block in auth.html. header is defined in index.html:
Your auth.html would look like:
{% extends "index.html" %}
{% block header %}
{{ block.super }}
Your extra stuff, which will come after whatever was in the header block
{% endblock %}

Django extends/include - bug?

I'm trying to use both extends and include tags in one template, just like:
{% extends "layout.html" %}
{% block content %}
<div id="content">
<nav class="mainMenu">
{% include "list.html" %}
</nav>
</div>
{% endblock %}
Unfortunately what is displayed is only list.html without contents from layout.html and file that is including the list.html. Why is that?
You are most probably only rendering list.html in your view, check for that.

Categories

Resources