Wrap jinja2 block content in intermediate template - python

Is it possible to calla child block from an intermediate parent template?
Example
// base.html
<body>
{% block content %}{% endblock %}
</body>
// base-for-pages.html
{% block content %}
<div class="wrap">
{% block content %}{% endblock %}
</div>
{% endblock %}
// template3.html
{% block content %}
<h1>Actual content</h1>
{% endblock %}
I know it doesn't work like this but is it possible to set up something like in base-for-pages.html so that you can wrap the contents of a child template before it's sent to the parent template?
It seems like overkill to create a new root template just to wrap the contents of a child 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.

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

Nested django templates

This seems like a pretty basic thing to do but although I've been using Django for around a year, I've never run into this scenario yet.
In a lot of templating/web frameworks, template inheritance works a bit differently, in that usually it behaves more like wrappers, so if you have childtemplate.html, parenttemplate.html, and grandparenttemplate.html, then the finally rendering usually looks something like:
grandparent header
parent header
child header
child content
parent content
parent footer
grandparent content
grandparent footer
That's not exactly how it works in Django but I'm wondering how to implement it.
Specifically I have my "child" template, let's just say it's foo.html. foo.html optional gets a variable parent_template or defaults to "base.html"
{% extends parent_template|default:"base.html" %}
{% block content %}
I am a foo and my title is {{ foo.title }}
{% endblock content %}
So, this is where I run into a snag. If parent_template is a template, that template should wrap the contents of foo.html and then place the result into base.html:
{% extends "base.html" %}
{% something_magical_here %}
<div>parent header</div>
# content of foo.html
<div>parent footer</div>
{% end_something_magical_here %}
And then in base.html:
<html>
... snip ...
<div id="content">
{% something_else_magical %}
# content of parent_template rendering, or just foo.html if no parent_template given
{% end_something_else_magical %}
Which should render as
<html>
... snip ...
<div id="content">
<div>parent header</div>
I am a foo and my title is Bar
<div>parent footer</div>
if parent_template is set and
<html>
... snip ...
<div id="content">
I am a foo and my title is Bar
if it is not.
I hope my problem is clear: I need to (optionally) wrap a template in a parent template, and then send the results of that to the base.html template.
Normally, something like this might work:
#foo.html
{% extends "parent.html" %}
{% block child_content %}
I am a foo and my title is {{ foo.title }}
{% endblock child_content %}
#parent.html
{% extends "base.html" %}
{% block content %}
parent header
{% block child_content %}{% endblock child_content %}
parent content
parent footer
#base.html
base header
{% block content %}{% endblock content %}
base content
base footer
However, since the parent_template could be blank, then sometimes base.html will be getting just the child_content block, not the content block.
Also, I'd like to be able to do this without having to create a bunch of sub-blocks (what if I decide that the foo application should have its own /foo/base.html that then calls /base.html)?
Any ideas?
the extends template tag can take a variable argument.
so:
base.html
{% block content %}
<p>BASE</p>
{% endblock %}
parent.html
{% extends "base.html" %}
{% block content %}
{{ block.super }}
<p>PARENT</p>
{% endblock %}
foo.html
{% extends ext_templ %}
{% block content %}
{{ block.super }}
<p>FOO</p>
{% endblock %}
using base:
return render_to_response('foo.html', {'ext_templ':'base.html'})
gives you:
<p>BASE</p>
<p>FOO</p>
using parent:
return render_to_response('foo.html', {'ext_templ':'parent.html'})
gives you:
<p>BASE</p>
<p>PARENT</p>
<p>FOO</p>
edit:
one way around this problem to get nested blocks is:
base.html
{% block content %}
{% block top %}
<p>BASE START</p>
{% endblock %}
{% block bot %}
<p>BASE END</p>
{% endblock %}
{% endblock %}
parent.html
{% extends "base.html" %}
{% block top %}
{{ block.super }}
<p>PARENT</p>
{% endblock %}
{% block bot %}
<p>PARENT</p>
{{ block.super }}
{% endblock %}
foo.html
{% extends ext_templ %}
{% block top %}
{{ block.super }}
<p>FOO</p>
{% endblock %}
{% block bot %}
<p>FOO END</p>
{{ block.super }}
{% endblock %}
The other method that i can think of is to wrap the blocks with an {% if ext_templ == 'parent.html' %} tag but that doesn't seem very dry. I'm curious to see other peoples response to nested blocks as well.
base.html
{% block content %}
<p>Grand Parent file</p>
{% endblock %}
parent.html
{% extends 'base.html' %}
{% block content %}
{% include 'grandchild1.html' %}
{% include 'grandchild2.html' %}
{% endblock %}
3.1. grandchild1.html
<div>Hello I'm grandchild 1</div>
3.2 grandchild1.html
<div>Hello I'm grandchild 2</div>
This way you can nest upto any level. write each components in sections in seperate .html files.
Include them to parent.html or to any other files.
I had three templates: gparent, parent, and gchild. Then I tested gparent and gchild as follows:
gparent had the following block hn tag. gchild extends gparent. There is no reference to parent.
{% block hn %}
Can you see me? # 1 naked text in the html, treated as a string.
{{ "No, I cant see you." }} # 2 A string literal in the variable template brace.
{% endblock hn %}
Both of these lines between the tags showed up on the child on screen. When the latter was not a string, Django threw a parsing error. That is because inside the double curly braces, Django is expecting a variable, or something like this string literal that it can evaluate.
When I added a block hn and this content to gchild, gchild's block hn content is all I saw.
{% block hn %}
Now I have my own hn block
{% endblock hn %}
Then I repeated this test, leaving gchild’s block hn in place, but taking away the content:
{% block hn %}
{% endblock hn %}
An empty, but present block hn on gchild blocked out (overrode) gparent's block hn content.
For this next test, I inserted parent between gparent and gchild. Now parent extends gparent, and gchild extends parent.
{% block metadata %}
{{ "You must eat your metadata to grow big and strong" }}
{% endblock metadata %}
Parent now has this block metadata tag, which is present in neither gparent nor gchild.
This content did not display.
Next I nested block metadata inside block hn on parent. gparent still has the two strings we tested from block hn a moment ago. gchild has an empty block hn.
As before, nothing displayed.
I will now remove block hn from gchild:
You must eat your metadata to grow big and strong
So, you can add a new block tag that is not present in an earlier template generation, so long as it is fully nested within an available block that the ancestor does define.
The sandwich generation will then pass that new tag and its content along to the child, which will display it if the child does not block (override) it.
Remember that the docs describe the content between the tags as a hole to be filled. If the child does not have the tag, the hole gets filled by the parent. This is what makes the parent’s content the default. This is also why most base templates contain the header, footer, navigation, and styling you want throughout the site. But with the same tag on the child, the child is filling the hole itself. Again, this is why a child can have its own block title tag, even though it is nested inside the html , which is typically inside the parent block header, or whatever you choose to call it.
I really struggled with this when I first learned Django, because I always wanted to do crazy stuff like
{% block first %}
{% block second %}
{% endblock first %}
{% endblock second %}
That will not work anywhere.

Categories

Resources