Interpolate Django template include variable - django

I'm doing something like
{% for part in parts %}
{% include "inc.html" with o=part prefix="part{{ forloop.counter0 }}_" %}
{% endfor %}
where inc.html could be something of such kind:
<p id="{{ prefix }}para">{{ o.text }}</p>
I just discovered the prefix variable isn't interpolated and "part{{ forloop.counter0 }}_" is passed literally.
Any relatively elegant work-around?

I think the best solution would be to register an inclusion_tag, that would handle the part and forloop.counter operations:
#register.inclusion_tag("inc.html")
def inc_tag(part, loop_counter):
prefix = 'part%s_' % (loop_counter,)
context = {
'part': part,
'prefix': prefix,
}
return context
And you would call it like that
{% for part in parts %}
{% inc_tag part=part loop_counter=forloop.counter0 %}
{% endfor %}
Your way is also doable like so, but I wouldn't recommend that
{% for part in parts %}
{% with "part"|add:forloop.counter0|add:"_" as prefx %}
{% include "inc.html" with o=part prefix=prefix %}
{% endwith %}
{% endfor %}

Related

Django template within a for loop

I have a for loop that looks like this:
<ul>
{% for post in latest_post_list %}
<li>{{ post }}</li>
</ul>
{% empty %}
<p>No posts are available.</p>
{% endfor %}
But I want to wrap each post object in a template, so for example:
{% for post in latest_post_list %}
{% include 'jobposttemplate.html' %}
{% empty %}
{% include 'noposts.html' %}
{% endfor %}
jobposttemplate.html will then include all the various information held within the JobPost model.
I tried:
{% for jobposttemplate.html in latest_post_list %}
{{ jobposttemplate.html }}
{% empty %}
{% include 'noposts.html' %}
{% endfor %}
But, predictably, it didn't work. Basically I'm just trying to avoid having to having to write the html out each time, e.g.:
{% for post in latest_post_list %}
<div style="blah">{{ post.deadline }}
</div>
<div style="schmah">{{ post.created_at }}
</div>
{% empty %}
<p>No posts are available.</p>
{% endfor %}
If you are using app specific subfolders inside your templates/ directory, you need to add them to the file name here too.
{% include 'myapp/jobposttemplate.html' %}
And if you want the template to have access to variables, you can add them to the template's scope using with. The sub-template also has access to all scope variables too.
{% include 'myapp/jobposttemplate.html' with object=post %}

How simplify calling include with multiple viariables in Django template?

I want to simplify (make more readable) Django template code:
{% comment %}
required variables:
group_id = 'exclude-brands-group'
select_name = 'exclude-brands'
entities = 'excludeBrandsSets'
add_keyword_name = {% trans 'Brand' %}
url_edit_keywords = {{ url_project_filter_exclude_brands_edit_keywords }}
{% endcomment %}
{% with group_id='exclude-keywords-group' select_name='exclude-keywords' %}
{% with entities=excludeKeywordsSets %}
{% trans 'Words' as add_keyword_name %}
{% with url_edit_keywords=url_project_filter_exclude_keywords %}
{% include 'web_site/seo/frontend/seo/filtered_keyword_idea/template/keyword_filter_group.html' %}
{% endwith %}
{% endwith %}
{% endwith %}
But I do not have idea how to reduce number of with tags - since include is only one line command. I have not idea how simplify trans. Do you know some simpler way to pass variables to templates?
The include tag lets you pass additional context to the template. That means you don't need separate with tags (although the downside is you end up with very long lines). There isn't any way to include the trans tag.
{% trans 'Words' as add_keyword_name %}
{% include 'web_site/seo/frontend/seo/filtered_keyword_idea/template/keyword_filter_group.html' with group_id='exclude-keywords-group' select_name='exclude-keywords' entities=excludeKeywordsSets url_edit_keywords=url_project_filter_exclude_keywords %}

Django Templates: how flexible are variables?

I need to have a variable in a teplate that is basically a counter for a for-loop. The problem is: I need to manipulate it, depending on the for-element I am dealing with, I will have to reset the counter (an IF inside the for-loop).
Is this doable inside a Django template?
This is basically what I would like:
{% i = 0 %}
{% for l in list %}
{% if i == 5 %}
{% i = 0 %}
Do Something
<br>
{% else %}
{% i = i + 1 %}
{% endif %}
{% endfor %}
You can't with the builtin tags:
http://www.mail-archive.com/django-users#googlegroups.com/msg27399.html
The following snippets might be a good starting point:
Template counters
Access by index
EDIT: For the record, OP needed a conditional with divisibleby. See the accepted answer here plus the comments in this answer.
What you want is the forloop.counter variable that Django's template language provides.
https://docs.djangoproject.com/en/dev/ref/templates/builtins/#for
You would do something like:
{% for element in list %}
{% if forloop.counter > 5 %}
Do something
{% else %}
Do something else
{% endif %}
{% endfor %}
If you want to do something cyclically, you're basically doing a modulo operator (http://en.wikipedia.org/wiki/Modulo_operation), unfortunately, Django Template doesn't quite have this, but it does allow a 'divisible by' operator.
https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#divisibleby
So you'll add:
{% if {{ forloop.counter|divisibleby:"5" }} %}
{{ whatever }}
{% endif %}

Django template: check for empty query set

Is there a way to check for an empty query set in the Django template? In the example below, I only want the NOTES header to be displayed if there are notes.
If I put an {% empty %} inside the "for" then it does display whatever is inside the empty tag, so it knows it's empty.
I'm hoping for something that does not involve running the query twice.
{% if notes - want something here that works %}
NOTES:
{% for note in notes %}
{{note.text}}
{% endfor %}
{% endif %}
Clarification: the above example "if notes" does not work - it still displays the header even with an empty query set.
Here's a simplified version of the view
sql = "select * from app_notes, app_trips where"
notes = trip_notes.objects.raw(sql,(user_id,))
return render_to_response(template, {"notes":notes},context_instance=RequestContext(request))
Edit: the view select selects from multiple tables.
Have a look at the {% empty %} tag.
Example from the documentation
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% empty %}
<li>Sorry, no athletes in this list.</li>
{% endfor %}
</ul>
Link: https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#for-empty
If you are interested in a table, or some kind of heading if there are results, add the forloop.first:
{% for athlete in athlete_list %}
{% if forloop.first %}
Athlete Name:
{% endif %}
{{ athlete.name }}
{% empty %}
Sorry, no athletes in this list.
{% endfor %}
Try {% if notes.all %}. It works for me.
In your view check whether notes is empty or not. If it is then you pass None instead:
{"notes": None}
In your template you use {% if notes %} as normal.
It's unfortunate that you're stuck using a raw query set - they're missing a lot of useful behavior.
You could convert the raw query set into a list in the view:
notes_as_list = list(notes)
return render_to_response(template, {"notes":notes_as_list},context_instance=RequestContext(request))
Then check it as a boolean in the template:
{% if notes %}
Header
{% for note in notes %}
{{ note.text }}
{% endfor %}
{% endif %}
You could also make it happen without conversions using forloop.first:
{% for note in notes %}
{% if forloop.first %}
Header
{% endif %}
{{ note.text }}
{% endfor %}
What about:
{% if notes != None %}
{% if notes %}
NOTES:
{% for note in notes %}
{{ note.text }}
{% endfor %}
{% endif %}
{% else %}
NO NOTES AT ALL
{% endif %}
Your original solution
{% if notes %}
Header
{% for note in notes %}
{{ note.text }}
{% endfor %}
{% endif %}
Works now with Django 1.7 and thanks to QuerySet caching, it does not cost and extra query.
Often the right way to do this is to use the {% with ... %} tag. This caches the query so it runs only once and also gives you more flexibility with your markup than using {% empty %}.
{% with notes as my_notes %}
{% if my_notes %}
<ul>
{% for note in my_notes %}
<li>{{ note }}</li>
{% endfor %}
</ul>
{% else %}
<p>Sorry, no notes available</p>
{% endif %}
{% endwith %}
With this particular example I'm not sure how useful it is but if you're querying Many-to-Many field, for instance, it's likely what you want to do.
Use {% empty %} in django templates
{% if list_data %}
{% for data in list_data %}
{{ data.field_1 }}
{% endfor %}
{% else %}
<p>No data found!</p>
{% endif %}
We can write above code with {% empty %}.
{% for data in list_data %}
{{ data.field_1 }}
{% empty %}
<p>No data found!</p>
{% endfor %}

Django {% with %} tags within {% if %} {% else %} tags?

So I want to do something like follows:
{% if age > 18 %}
{% with patient as p %}
{% else %}
{% with patient.parent as p %}
...
{% endwith %}
{% endif %}
But Django is telling me that I need another {% endwith %} tag. Is there any way to rearrange the withs to make this work, or is the syntactic analyzer purposefully carefree in regards to this sort of thing?
Maybe I'm going about this the wrong way. Is there some sort of best practice when it comes to something like this?
if you want to stay DRY, use an include.
{% if foo %}
{% with a as b %}
{% include "snipet.html" %}
{% endwith %}
{% else %}
{% with bar as b %}
{% include "snipet.html" %}
{% endwith %}
{% endif %}
or, even better would be to write a method on the model that encapsulates the core logic:
def Patient(models.Model):
....
def get_legally_responsible_party(self):
if self.age > 18:
return self
else:
return self.parent
Then in the template:
{% with patient.get_legally_responsible_party as p %}
Do html stuff
{% endwith %}
Then in the future, if the logic for who is legally responsible changes you have a single place to change the logic -- far more DRY than having to change if statements in a dozen templates.
Like this:
{% if age > 18 %}
{% with patient as p %}
<my html here>
{% endwith %}
{% else %}
{% with patient.parent as p %}
<my html here>
{% endwith %}
{% endif %}
If the html is too big and you don't want to repeat it, then the logic would better be placed in the view. You set this variable and pass it to the template's context:
p = (age > 18 && patient) or patient.parent
and then just use {{ p }} in the template.