Jinja's loop variable is not available in include-d templates - python

I have code similar to the following in one of my jinja template
{% for post in posts %}
{% include ["posts/" + post.type + ".html", "posts/default.html"] %}
{% endfor %}
which is supposed to render each post inside the posts collection, depending on the .type of the post. I have a different template setup for each post.type. And for those I don't have a template, it reverts to the default post template.
Now, I want the index of the post being displayed from bottom, inside the post templates, which is provided by loop.revindex. But for some reason, if I use loop.revindex inside the post template, I get a error saying UndefinedError: 'loop' is undefined.
So, is loop not available in the included templates? Is this by design? Am I doing something wrong with how I organised my templates for this to be not available?
Edit Okay, I came up with a workaround, in the for loop, before I include my template, I do
{% set post_index = loop.revindex %}
and use post_index inside the post template. Not ideal, but seems like the only way. I still want to know your solutions though.
Edit 2 One other thing, I am able to access the post variable inside the included template, but not the loop variable.

If might be possible with the {% with %} statement.
Try this:
{% with %}
{% set loop_revindex = loop.revindex %}
{% include ... %}
{% endwith %}
Instead of using loop.revindex in the included template, use loop_revindex.

Another option is to pass the entire loop variable into the included template by setting a local variable to loop
{% for post in posts %}
{% set post_loop = loop %}
{% include ["posts/" + post.type + ".html", "posts/default.html"] %}
{% endfor %}
This gives you access to all of the loops properties, and, to me, makes it more clear in the included template what the variable is.

Related

Django: Is it possible to call a template from inside another template? [duplicate]

I have a very basic template (basic_template.html), and want to fill in the with data formatted using another partial template. The basic_template.html might contain several things formatted using the partial template.
How should I structure the code in views.py?
The reason I am doing this is that later on the will be filled using Ajax. Am I doing this right?
You can do:
<div class="basic">
{% include "main/includes/subtemplate.html" %}
</div>
where subtemplate.html is another Django template. In this subtemplate.html you can put the HTML that would be obtained with Ajax.
You can also include the template multiple times:
<div class="basic">
{% for item in items %}
{% include "main/includes/subtemplate.html" %}
{% endfor %}
</div>
You can do this using a block. Blocks are a Django Template tag which will override sections of a template you extend. I've included an example below.
basic_template.html
<body>
{% block 'body' %}
{% endblock %}
</body>
template you want to include: (i.e. example.html)
{% extends 'basic_template.html' %}
{% block 'body' %}
/* HTML goes here */
{% endblock %}
views.py:
return render_to_response(template='example.html', context, context_instance)
Doing this will load basic_template.html, but replace everything inside of {% block 'body' %} {% endblock %} in basic_template.html with whatever is contained within {% block 'body' %} {% endblock %}.
You can read more about blocks and template inheritance in the Django Docs
There are mainly 2 ways (2 easy ones)
1:
In base html put
{% include "myapp/sub.html" %}
And just write html code inside your sub.html file
2:
https://docs.djangoproject.com/en/dev/ref/templates/language/#template-inheritance
I just wanted to add differences of extend and include.
Both template and include can use models inserted in current app.
Template is for global usage by your any app. Include is for use in certain apps.
For ex: you want to insert Image Slider to your homepage and about page but nowhere else. You can create Slider app with its own model for convenience and import its model and include in that pages.
If you used template for this example, you would create 2 templates one with slider and everything else other template have.

Django inclusion tag not showing data

I'n trying to create inclusion tag so I can display data in my navigation bar on every page. The tag will be included in the "base.html" so that way it should display everywhere.
tags.py
#register.inclusion_tag('menu.html')
def show_hoods(HoodList):
gethoods = Hood.objects.all()
return {'gethoods': gethoods}
menu.html
{% for hood in gethoods %}
<h3>{{ hood.name }}</h3>
{% endfor %}
For some reason the menu.html template is blank and is not showing any data.
Also, once I have the menu.html running, will simple {% include 'menu.html' %} work inside the base.html? Will that be automatically rendered?
Edit:
Based on the feedback below, the code above is correct, however the base.html code was incorrect as the inclusion_tag is not loaded with {% include %} but {% load %} is used instead.
corrected base.html
{% load tags %}
{% show_hoods hoodlist %}
Thanks for the feedback!
Directly viewing the menu.html template will not display anything as it has no context variables set. gethoods will be empty so there will be nothing for the for loop in the template to loop over.
One of the main purposes of an include tag is to set extra context variables and then render a template using these variables. Directly viewing the template will show the template without the variables, but including the include template ({$ show_hood %} in your case) will add the context variables (gethoods) and render the template using them.
Answering your second question, you add include templates using their name, (the name of the function by default) rather than the {% include %} tag. The {% include %} tag is for when you simply want to render one template inside of another, and where it either doesn't need any context variables or uses the context variables available to its parent template.

Change Django Template Inheritance Dynamically?

I have a template with the following: {% extends "main/main-template.html" %}
I also want a template with the exact same thing, only instead it {% extends "main/main-template-quick.html" %} It seems like a violation of DRY to just copy and paste the same code into a new file, just so I can change the template. Is there any way to select the super-template dynamically?
If not, is there a good way to do the following: Reuse the same {% block %} and its content with a different template. At the same time, not violating DRY.
I'm also open to other template languages that may be able to do this.
If you check the docs you'll see that extends accepts a variable too.
{% extends variable %} uses the value of variable. If the variable
evaluates to a string, Django will use that string as the name of the
parent template.
So you can easily determine the appropiate base template in your view and pass it to your template.
And if you want to reuse a chunk of html in different contexts than the include tag is your friend.
Django allows for display the parent templates block content using {{ block.super}}.
This allows you to insert the parent's block contents.
{% block content %}
{{ block.super }}
{% endblock content %}
block.super was designed to allow you to
Reuse the same {% block %} and its content with a different template.

Catching TemplateDoesNotExist in Django

I am trying to use the templatetag described in SO answer: https://stackoverflow.com/a/6217194/493211 in a project using Django 1.4.3 (with Python 2.7.2).
I adapted it like this:
from django import template
register = template.Library()
#register.filter
def template_exists(template_name):
try:
template.loader.get_template(template_name)
return True
except template.TemplateDoesNotExist:
return False
So that I could use it like this in another template:
{% if 'profile/header.html'|template_exists %}
{% include 'profile/header.html' %}
{% else %}
{% include 'common/header.html' %}
{% endif %}
This way, I could have avoided using solutions such as changing the order of my apps in INSTALLED_APPS.
However, it does not work. If the template does not exist, then the exception is raised within the stack/console but it is not propagated up to get_template(..) (from inside this statement), and thus not to my foolish API. Hence, this blows up in my face during the rendering. I uploaded the stacktrace to pastebin
Is this a wanted behavior from Django?
I ended up stop doing foolish things as is. But my question would remain.
What about a custom tag? This doesn't provide the full functionality of include but seems to meet the needs in the question.:
#register.simple_tag(takes_context=True)
def include_fallback(context, *template_choices):
t = django.template.loader.select_template(template_choices)
return t.render(context)
Then in your template:
{% include_fallback "profile/header.html" "common/header.html" %}
I found some kind of an answer to my question so I am posting it here for future refence.
If I use my template_exists filter like this
{% if 'profile/header.html'|template_exists %}
{% include 'profile/header.html' %}
{% else %}
{% include 'common/header.html' %}
{% endif %}
and if profile/header.html does not exist, then the TemplateDoesNotExist gets strangely propagated at page load and I get a server error. However, if instead, I use this in my template:
{% with 'profile/header.html' as var_templ %}
{% if var_templ|template_exists %}
{% include var_templ %}
{% else %}
{% include 'common/header.html' %}
{% endif %}
{% endwith %}
Then, it works like a charm!
Obviously, I could have used
django.template.loader.select_template(['profile/header.html','common/header.html'])
in the view (from this SO answer). But I am using a CBV which I wanted to keep rather generic and this was called from the main template. And also I thought it would be nice to have my site working if this apps goes down for whatever reason. If this seems silly to you, please leave a comment (or yet a better answer).

django philosophy: when to include templates and when to have code generate html?

When using Django templates, should I have some templates that act like "subroutines", so to speak, or should I generate HTML from within my code in these cases?
For example, I have a template with several lists of names, each of which I want to turn into a select. Should I have a template that renders the name_list variable into a select, and do something like this:
#in the view:
return {'name_list_1': name_list_1,
'name_list_2': name_list_2,
'name_list_3': name_list_3}
#in the template:
{% with name_list_1 as name_list %}
{% include "sub_name_list_select.html" %}
{% endwith %}
{% with name_list_2 as name_list %}
{% include "sub_name_list_select.html" %}
{% endwith %}
{% with name_list_3 as name_list %}
{% include "sub_name_list_select.html" %}
{% endwith %}
Or should I have a function in my code, name_list_to_select_html, which does the same job, and do this:
return {'name_list_1_html': name_list_to_select_html(name_list_1),
'name_list_2_html': name_list_to_select_html(name_list_2),
'name_list_3_html': name_list_to_select_html(name_list_3)}
#in the template:
{{ name_list_1_html|safe }}
{{ name_list_2_html|safe }}
{{ name_list_3_html|safe }}
Or are both of these wrong and I am getting the philosophy totally wrong?
Additional question: in terms of speed, is it slow to constantly include templates? Is that a bonus point for the in-code html generation?
Generally, HTML should only be generated in the templating system or directly related code. That keeps the view of the data completely separate from the business and functional logic. I feel that's a proper separation of concerns. Go with your first solution.
As for performance, Django should probably take around the same amount of time running either code. But it has built-in view and template fragment caching if you know those segments of code don't need to be regenerated on every request.

Categories

Resources