Django Template inheritance: how many levels and what page to render - django

I would like to have some advice in constructing django template levels.
Reading the docs, I do not understand how to create a template hierarchy structure with more than 2 levels.
For example, I would like to create 3 levels of inheritance:
base.html
└── base_level2.html
├── level2_level3_1.html
└── level2_level3_2.html
This is my code:
base.html
First level
{% block level2 %}{% endblock %}
base_level2.html
{% extends "base.html" %}
{% block level2 %}
Second level
{% block level3_1 %}{% endblock %}
{% block level3_2 %}{% endblock %}
{% endblock %}
level2_level3_1.html
{% extends "base_level2.html" %}
{% block level3_1 %}
Third level, part 1
{% endblock %}
level2_level3_2.html
{% extends "base_level2.html" %}
{% block level3_2 %}
Third level, part 2
{% endblock %}
views.py:
def myView(request):
return render_to_response('level2_level3_1.html', {}, context_instance=RequestContext(request))
In this way I can see the following on my browser:
First level
Second level
Third level, part 1
And this is logical to me because I call render_to response only on level2_level3_1.html.
Of course, if call level2_level3_2.html, I get the message Third level, part 2 but not the Third level, part1.
How to solve this? Is that a good approach? I've structured stuff in this way because my real templates are very big, a lot of lines of code, so I would like to keep some order.
Any advice will be appreciated.

It's hard to say if it's a good or bad idea or not without knowing the specific functionality of your templates, but my immediate reaction is that you're trying to over organize your templates. I think most people would urge you away from more than a 3-tier system because it makes it more difficult to make small changes in the website and more difficult to keep track of where things are. from the Zen of Python:
Flat is Better than Nested
The recommendation for a 3-tier system inTwo Scoops of Django goes like this:
Each app has a base_<app_name>.html template. App-level base templates share a common parent, base.html.
Templates within apps share a common parent base_<app_name>.html template.
Any template at the same level as base.html inherits base.html
and for your naming schema, it might look like this:
| Templates/
|--base.html
|--someothertemplate.html # extends base.html
|--level2/
|----base_level2.html # extends base.html
|----level2_1.html # extends base_level2.html
|----level2_2.html # extends base_level3.html
EDIT: and there's no real reason for this:
Second level
{% block level3_1 %}{% endblock %}
{% block level3_2 %}{% endblock %}
where each block refers to the content of one template. you can simplify that to one block like
{% block level3 %}{% endblock level3%}
and then in each of the level3 templates, rename the blocks accordingly

Probably not the best way of doing it but you might user include https://docs.djangoproject.com/en/dev/ref/templates/builtins/#include
something like this for base_level2.html
{% extends "base.html" %}
{% block level2 %}
Second level
{% include "level2_level3_1.html" %}
{% include "level2_level3_2.html" %}
{% endblock %}
i've not tested this, so not sure it works.
and btw:
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.

Related

django: using blocks in included templates

I have some html structures that are reused in quite a few different places. It's different from a overall template so I can't extend it, it can also be used to contain complicated content so I don't think defining it as a template tag does a good job. Below is some pseudo code describing my desired outcome, when using template_level2.html you can easily put stuff into the reusable_pattern_template by calling the block inside it. If I do use this code, what you write in 'actual content' of template_level_2.html wouldn't show up. How should I deal with this?
base.html
<html>
<head></head>
<body>
{% block content %}{% endblock %}
</body>
</html>
template_level1.html
{% extends 'base.html' %}
{% block content %}
Something here...
{% include 'reusable_pattern_template.html' %}
Something else here...
{% endblock %}
reusable_pattern_template.html
<div>
<div>
<div>
{% block local_content %}{% endblock %}
</div>
</div>
</div>
template_level2.html
{% extends 'template_level1.html' %}
{% block local_content %}
Actual content here...
{% endblock %}
update:
Sorry, the extends in template_level2.html has some misspelling, I've just corrected it.
It may not be very clear, but the code above is more of a pseudo code describing my desired outcome. In short,
I would like to include small pieces of reusable html patterns in my
templates.
These patterns are like boxes, that you can put whole pieces of html
content in them. So context variables may be a bit of too limited for
my purpose
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. (Django template tag documentation)
I came across this problem and ended up with the following compromise, hoping someone else might find it useful. It relies on using with blocks in the child templates.
base.html wants to reuse a common nav.html include, but define some blocks where variables inside nav.html might be overriden by child templates.
<!-- base.html: -->
<html>
[...]
<nav class="desktop">
{% block desktop_nav %}
{% include "includes/nav.html" %}
{% endblock %}
</nav>
[...]
<nav class="mobile">
{% block mobile_nav %}
{% include "includes/nav.html" %}
{% endblock %}
</nav>
[...]
The include template depends on a variable called selected, which base.html does not define, by default:
<!--includes/nav.html:-->
About
People
Contact
But child pages can override that value as follows:
<!--about.html:-->
{% extends "base.html" %}
{% block desktop_nav %}{% with selected='about' %}{{ block.super }}{% endwith %}{% endblock %}
{% block mobile_nav %}{% with selected='about' %}{{ block.super }}{% endwith %}{% endblock %}
so, not perfect, I still have to have two separate blocks and use those with blocks twice, but it does allow me to override variables in include blocks from the parent template.
It seems that the final template is trying to extend itself (if it was in quotes).
You really don't need that much of complexity. It's actually pretty much simpler.
The base template should hold the skeleton of your template, then you can extend it to make customizations. For reusable code blocks that you don't want to include in your every view, include them where appropriate but don't use any block, extends or include statement within the included file. Django will not parse those but the context variable passed from the view can still be used.
In short, you can create variables in whichever template you're planning to include e.g.
{{ localcontent }}
and then assign those variables wherever you include the template e.g.
{% include "name_snippet.html" with localcontent="Actual content" %}
You could split reusable_pattern_template into begin and end templates. Then in level1 you can go include begin, block, include end.
Alternatively you could pass a template name into reusable_pattern_template as a context variable and then include it in reusable_pattern_template. This will require changing the relationship between level1 and level2 in your example but is generally more powerful.
Not the exact thing but you can do this (I've used it for including navigation bars):
Use with and only
snippet.html:
<div>
{{ listItem1 }}{{ listItem2 }}...{{ listItemN }}
</div>
index.html:
{% include 'snippet.html' with listItem1='<li>HOME</li>' listItem2 ='<li>ABOUT</li>' only %}
Since, we have used only after listItem2, all further variables will be ignored.

How to create a custom Django template tag working outside of a block?

In my project, I need to treat CSS requirements differently depending on several parameters. Thus, I need an additional level of abstraction compared to available static management systems.
In my templates, I would like to be able to register CSS (and JS) requirements with a template tag which would look like {% register_css 'myfile.css' %}. This approach works like a charm.
However, there is still one problem with this approach : the tag works perfectly when inserted in a block, but never gets called when inserted on top of a child template. The tag definition is:
#register.simple_tag(takes_context = True)
def register_css(context, *args):
context['static_registry'].register_css(streamlet)
return ''
This is my test template:
{% extends main_skelton %}
{% load static_registry %}
{% register_css 'base' %}
{% block title %}Welcome{% endblock %}
{% block content %}
{% register_css 'home' %}
<p>Some content here</p>
{% endblock %}
In that template, the 'home' css gets registered, but not the 'base' one. How may I make both work?
Thanks!
Include an empty {% block init %} in your base template, then override that block in the child templates to register files - {% block init %}{{ block.super }} {% register_css 'foo %}{% endblock %}
The underlying issue is that in a child template, only blocks that match something in the parent template will get rendered. AFAIK there's no easy way around that, but using a dummy block works just fine.

Django: How to include a block site wide?

Apologies, this is pretty basic. I have abstracted my static html navigation bar to a block so that it can be dynamically rendered from a model. I have created a new block marker in base.html with the following syntax
{% block navigation %}{% endblock %}
How can I ensure this is rendered on every page? Do I need to create some sort of middle layer for this? Everything I have done so far has simply used the primary block.
EDIT Sunday, 14 August 2011 11:25 AM
I didn't explain this very well. The content of navigation block is
{% extends 'base.html' %}
{% block navigation %}
<nav>
<ul>
{% for item in items %}
<li>{{ item.name }}</li>
{% endfor %}
</ul>
</nav>
{% endblock %}
I want to render this on every page without having to go through child templates of base.html individually and add it to them, if that is possible.
You have got static navigation bar, so you can just write code in your base.html page and then use it with {% extends 'base.html' %} tag on every new page. Your markup will be in every page. Also, if you use only extends tag in your child page and then render it - you will see base.html without any edition.
If you have got code, which need to appear on few pages, but not on everyone - create 'includes' directory, save code there and extend your base template with {% include %} tag. It won't avoid repating, but make your code shorter.
And last thing you'll need in future, maybe with dynamic code - caching. With {% cache %} tag you can cache block for some time.
Your block is not doing anything here, but if you want something to appear in all the templates, you have to define it in a base template (base.html) and make the others extend from it:
base.html
Yor menu and the stuff you want to appear everywhere
{% block content %}{% endblock %}
Another template
{% extends "base.html" %}
{% block content %}
The actual content of the page
{% endblock %}
{% block FOO %}{% endblock %} reserves a space to be overwritten in sub templates.
{% include "foo.html" %} pulls content from another file into the current file.
To get {% block navigation %}{% endblock %} to display on every page, you need to add content
{% block navigation %}SHOW ME{% endblock %}

Override inner template block in Django

I have the following structure of templates (simplified for clarity):
base1.html:
<html>
<head>{% block head %}{% endblock %}</head>
<body>{% block body %}{% endblock %}</body>
</html>
base2.html:
{% extends "base1.html" %}
{% block head %}
<meta .... />
<title .... />
css includes etc.
{% endblock %}
{% block body %}
{% block header %}{% endblock %}
{% block featured %}{% endblock %}
{% block navigation %}{% endblock %}
{% block content %}{% endblock %}
{% block footer %}{% endblock %}
{% endblock %}
Also base3.html and base4.html which futher detalize the generic body structure defined in previous base templates (not shown here). The last template extends base4.html, overriding blocks with concrete content (markup is handled by base templates).
The question is: I have two templates: main.html and article.html which both extend base4.html. But in article.html I want the order of body blocks defined in base2.html to be different (featured block to go after navigation). How can I achieve that? Or how to refactor the structure of templates to make this possible? Overriding the block body in top-level template doesn't work.
I don't think that you're going to have any luck going about it that way. A simple solution could be to have an optional variable that base2.html looks at, which determines the alternate order. In fact it may even be that you can define the presence of this variable in the article.html template itself. I haven't tried this, but something like the following may work:
{% with alternate_order=1 %}
{% include base4.html
{% endwith %}
Why do you have so many levels of inheritance? As a note, django provides no limitations, but typically we use the three-level approach. See docs
Here are a few suggestions:
You can probably combine base1.html and base2.html.
If you can put the html code in the parent then do so. I usually implement my templates like this:
<div id='sidebar'>{% block sidebar_content %}{% endblock %}</div>
As for your problem, if you need a different layout, that's when you branch. That is, if using a different css won't work.

Why were the original authors of Django against include tags?

In this excellent Google Tech Talk by Jacob Kaplan-Moss, Jacob says that they added support for the include template tag despite previous dogmatic objections, and says that people shouldn't use it.
Does anyone know why? A quick search didn't show anything that would explain why. There's nothing relevant in the now-fixed ticket where support for an include tag was added. I use include tags liberally to avoid repeating myself, which seems like a good idea, but maybe I'm missing some core reason why those who know think it's bad.
I suppose he wants to encourage template reuse by inheritance (using extends) rather than by composition. Perhaps the implication is that if you can't organise your templates this way, the dogmatic opinion is that your site is organised poorly. (For example, if you are reusing a navigation menu, shouldn't it always be in the same place in the page structure? Why should each individual page decide where to put it?)
By the way, using include doesn't do much to help you stay DRY, because any context that the included template requires must be passed from all the views that use it.
By contrast, using a custom inclusion template tag allows you to execute arbitrary Python code at the point where the tag is included, rather than in the view (or by shoving it into a model just to make it easier to access in the template).
As a trivial example, I wanted to show a list of users' avatars. Using include, it looks like this:
{% for user in users %}
{% with user.gravatar_url as avatar_url %}
{% include "foo/bar/avatar.html" %}
{% endwith %}
{% endfor %}
With a custom tag:
{% for user in users %}
{% gravatar user.email %}
{% endfor %}
Using the custom inclusion tag meant that the Gravatar hashing logic no longer had to be a concern of the User model, nor of the view function.
This said, I think are are some situations where you inevitably have similar data in the context of multiple templates, you don't need to do anything fancy with it, you just want to display some of its attributes so you don't want to write a function to make it work.
For example, I wrote a blog application (who hasn't?) which had two types of archive view: a basic sequential, X-posts-per-page one, and a monthly archive view. Both templates obviously had a list of posts in their context, both used exactly the same summary template fragment to show a title and excerpt from each post, but each template presented them in a slightly different context. So I used:
{# in archive_index.html #}
{% extends "base.html" %}
{# some stuff specific to sequential archives here #}
{% for post in posts %}
{% include "post_summary.html" %}
{% endfor %}
{# probably more stuff specific to sequential archives #}
And...
{# in archive_monthly.html #}
{% extends "base.html" %}
{# some stuff specific to monthly archives here #}
{% for post in posts %}
{% include "post_summary.html" %}
{% endfor %}
{# probably more stuff specific to monthly archives #}
It really seems that in this case, composition makes more sense than inheritance. In fact it was difficult at first to imagine how inheritance would work here at all. Well, it's still possible:
{# in base_archive.html #}
{% extends "base.html" %}
{% block archive_header %}{% endblock %}
{% for post in posts %}
{% include "post_summary.html" %}
{% endfor %}
{% block archive_pagination %}{% endblock %}
Now, the two different archives extend this and just inject their unique stuff into the blocks:
{# in archive_monthly.html #}
{% extends "base_archive.html" %}
{% block archive_header %}
<h1>Archive for {{ month }}</h1>
{% endblock %}
{% block archive_pagination %}
{# previous/next month links here #}
{% endblock %}
I'll leave imagining what archive_index.html looks like as an exercise for the (no doubt bored) reader.
Phew! It feels smart to have come up with a way of doing the same thing using both composition and inheritance, but is the latter just bending over backwards to conform to the dogma that Jacob Kaplan-Moss mentioned?
Having just watched the video (yes, all 1 hour 5 minutes of it, just so I could finish answering this question), I don't think Jacob would be enormously bothered. It sounded like an off-the-cuff comment, maybe a reference to which technique you ought to consider first.