Django Nested Template Tags - python

I have a custom template tag that accesses a models function. However, I also need the custom template tag to be in a for loop, which requires nested template tags:
{% load custom_tags %}
{% for list in remind_lists %}
<h3>{{ list.title }}</h3>
{% for item in {% get_list_items user.username %} %}
<p>{{ item.title }}</p>
{% endfor %}
{% endfor %}
It gives me a TemplateSyntaxError- 'for' statements should use the format 'for x in y': for item in {% get_list_items user.username. Is there anyway I can do this?
custom tag:
register = template.Library()
#register.simple_tag
def get_list_items(event_ins, authenticated_user):
return event_ins.get_list_items(authenticated_user)

You can't nest tags in this way - but you can assign the output of the tag to a variable that you can then loop over:
{% load custom_tags %}
{% for list in remind_lists %}
<h3>{{ list.title }}</h3>
{% get_list_items list user.username as list_items %}
{% for item in list_items %}
<p>{{ item.title }}</p>
{% endfor %}
{% endfor %}

# you can format the text or data in the function itself and return the same to the template
{% for list in remind_lists %}
<h3>{{ list.title }}</h3>
{{ list.id|get_list_items:authenticated_user }}
{% endfor %}
register = template.Library()
#register.simple_tag
def get_list_items(event_ins, authenticated_user):
# you can format the text or data here
return ...

Related

django regroup with duplicate nametuple

I have code for regroup in django template, like below:
{% regroup clients by title.0 as clients_list %}
{% for client in clients_list %}
{{ client.grouper }}
{% for item in client.list %}
{% if item.client_link %}
{{ item.title }}
{% else %}
{{ item.title }}
{% endif %}
{% endfor %}
{% endfor %}
Sometime I have duplicate nametuple in my results for the same letter, for example - 'd':
GroupedResult(grouper='d', list=[<Client: decompany_1>]) GroupedResult(grouper='d', list=[<Client: decompany_2>])
so in result I have two headers with letter 'd'
Someone know, how to avoid this?
edit
views.py
class ClientsListView(ListView):
model = Client
template_name = 'clients/clients_list.html'
context_object_name = 'clients'

HTML between extension tags

Lets say I have an extension in Jinja. I want the extension to have the form:
{% start %}
<h1>{{ something }}</h1>
<p>{{ something.else }}</p>
{% for content in lst %}
<h3>{{ i.name }}</h3>
{% endfor %}
{% end %}
In my extension, I want to have access to the raw text between start and end, so this:
<h1>{{ something }}</h1>
<p>{{ something.else }}</p>
{% for content in lst %}
<h3>{{ i.name }}</h3>
{% endfor %}
I would like that in my extension. How could I do that? I have poured over the jinja documentation to no avail.
If your desire output is {{something}}, then you can use "raw" block in jinja.
{% raw %}
<h1>{{ something }}</h1>
{% endraw %}
It will print "{{something}}" (with curly braces).

Django {% blocktrans %}: How to handle pluralization inside a for loop?

I have the following loop in my Django template:
{% for item in state.list %}
<div> HTML (CUSTOMERS BY STATE) </div>
<!-- print sum of customers at bottom of list -->
{% if forloop.last %}
<h4>{{ forloop.counter }} Valued Customers</h4>
{% endif %}
{% endfor %}
Obviously, if I end up with only one customer, I'd like to print singular "Valued Customer"
According to Django's docs, one should use blocktrans. Tried the following, a few flavors of nesting:
{% blocktrans count %}
{% if forloop.last %}
<h4>
{{ forloop.counter }}
Valued Customer
{% plural %}
Valued Customers
</h4>
{% endif %}
{% endblocktrans %}
Keep getting TemplateSyntaxError: Invalid block tag: 'blocktrans', expected 'empty' or 'endfor'
Is there no way to combine with another loop? Any ideas how to solve? Thanks!
Here is the working code, thanks to alko:
{% load i18n %}
<!-- ... -->
{% if forloop.last %}
<h4>
{{ forloop.counter }}
{% blocktrans count count=forloop.counter %}
Valued Customer
{% plural %}
Valued Customers
{% endblocktrans %}
</h4>
{% endif %}
Probably, you forgot to load translation tags. Add following line at the top of your template:
{% load i18n %}
After you fix that, note that for a blocktrans tag after count a variable, whose value will serve for plural detection, should be specified, so you probably need something like
{% blocktrans count count=forloop.counter %}
To pluralize use this:
Customer{{ forloop.counter|pluralize }}

Django related_name

I'm trying to do something like:
{% for property in current_listing %}
{% for property_image in property.property_images.all %}
{% endfor %}
{% endfor %}
But I would like something like:
{% for property in current_listing %}
{% for property_image in property.property_images.**ORDER_BY('-order')[0]** %}
{% endfor %}
{% endfor %}
How can I do this?
If I understand what you want, you can try custom template filter:
from django import template
register = template.Library()
#register.filter
def get_first_ordered_by(queryset, order):
return queryset.order_by(order)[0]
Then on a template:
{% load my_tags %}
{% with image=property.property_images.all|get_first_ordered_by:'-order' %}
{{ image }}
{% endwith %}
Note, that you can not use {% for %} since result of get_first_ordered_by is not iterable.
You can add a method to you Model's class definition that returns the query you want, then cann that method from you template.

How can I concatenate forloop.counter to a string in my django template

I am already trying to concatenate like this:
{% for choice in choice_dict %}
{% if choice =='2' %}
{% with "mod"|add:forloop.counter|add:".html" as template %}
{% include template %}
{% endwith %}
{% endif %}
{% endfor %}
but for some reason I am only getting "mod.html" and not the forloop.counter number. Does anyone have any idea what is going on and what I can do to fix this issue? Thanks alot!
Your problem is that the forloop.counter is an integer and you are using the add template filter which will behave properly if you pass it all strings or all integers, but not a mix.
One way to work around this is:
{% for x in some_list %}
{% with y=forloop.counter|stringformat:"s" %}
{% with template="mod"|add:y|add:".html" %}
<p>{{ template }}</p>
{% endwith %}
{% endwith %}
{% endfor %}
which results in:
<p>mod1.html</p>
<p>mod2.html</p>
<p>mod3.html</p>
<p>mod4.html</p>
<p>mod5.html</p>
<p>mod6.html</p>
...
The second with tag is required because stringformat tag is implemented with an automatically prepended %. To get around this you can create a custom filter. I use something similar to this:
http://djangosnippets.org/snippets/393/
save the snipped as some_app/templatetags/some_name.py
from django import template
register = template.Library()
def format(value, arg):
"""
Alters default filter "stringformat" to not add the % at the front,
so the variable can be placed anywhere in the string.
"""
try:
if value:
return (unicode(arg)) % value
else:
return u''
except (ValueError, TypeError):
return u''
register.filter('format', format)
in template:
{% load some_name.py %}
{% for x in some_list %}
{% with template=forloop.counter|format:"mod%s.html" %}
<p>{{ template }}</p>
{% endwith %}
{% endfor %}
You probably don't want to do this in your templates, this seems more like a views job: (use of if within a for loop).
chosen_templates=[]
for choice in choice_dict:
if choice =='2':
{% with "mod"|add:forloop.counter|add:".html" as template %}
template_name = "mod%i.html" %index
chosen_templates.append(template_name)
Then pass chosen_templates to your template where you will have only
{% for template in chosen_templates %}
{% load template %}
{% endfor %}
Also, I don't quite understand why you are using a dict to select the template with a number that is not in the dictionnary. for key,value in dict.items() may be what you are looking for.
Try without using the block "with"
{% for choice in choice_dict %}
{% if choice =='2' %}
{% include "mod"|add:forloop.counter|add:".html" %}
{% endif %}
{% endfor %}

Categories

Resources