Django dictionary in templates: Grab key from another objects attribute - python

I have a dictionary called number_devices I'm passing to a template, the dictionary keys are the ids of a list of objects I'm also passing to the template (called implementations). I'm iterating over the list of objects and then trying to use the object.id to get a value out of the dict like so:
{% for implementation in implementations %}
{{ number_devices.implementation.id }}
{% endfor %}
Unfortunately number_devices.implementation is evaluated first, then the result.id is evaluated obviously returning and displaying nothing. I can't use parentheses like:
{{ number_devices.(implementation.id) }}
because I get a parse error. How do I get around this annoyance in Django templates?
Thanks for any help!

A workaround could be using the keys from number_devices and check in the for loop if it is equal to the key provided by number_devices.
{% for key in number_devices.keys %}
{% for implementation in implementations %}
{% ifequal key implementation.id %} you got it {% endifequal %}
{% endfor %}
{% endfor %}
Seems a bit ugly, but should work.

Related

Extracting values from a dict - flask and jinja

In my template I have
<p>{% for dict_item in MySQL_Dict %}
{% for key, value in dict_item.items() %}
{{value}}
{% endfor %}
{% endfor %}</p>
Which outputs 43.8934276 -103.3690243 47.052060 -91.639868 How do I keep values i.e. the coordinates together AND have them separated by a comma? Is this possible?
The easiest way is to format it on back-end and pass it the jinja in the way you want. In fact its better to put complex logic on the back-end not the front-end performs better and in jinja's case you don't have to pass in python methods so it's cleaner... other templating frameworks like tornados include all of pythons base functions so you don't have to pass them in like the example below. Another way to do it is to pass the str.join, len, and range method to jinja e.x join=str.join, len=len, range=range then... and I'm guessing MySQL_Dict is a 2d array, hence the double for loop
<p>
{% for dict_item in MySQL_Dict %} // dict_item == [{}, {}, ...]
{% for i in range(len(dict_item)) %} // dict_item[i] == {some object}
{{",".join(dict_item[i]["latitude"], dict_item[i]["longitude"])}}
{% endfor %}
{% endfor %}
</p>

Jinja List Issue

I am getting a weird problem in Jinja, I have a list endpoints, which contains dictionary for every endpoint. In each dictionary, there is a key tags which is a list. Every item in tags is itself a dictionary where the key value gives the label of a tag. endpoint may have similar tags.
A sample abstract representation of an endpoints object can be:
[ {"tags":[{"value":"car"},{"value":"place"}]} , {"tags":[{"value":"van"},{"value":"place"}]} ]
what I want is to simple display unique tags in a div. It is simple, keeping a list of all displayed tags and upon getting a tag, checking if it is already in the list, and if not display it and add it to the list. Weirdly, it's not working.
The codes are:
{% set tagValues = [] %}
{% for endpoint in endpoints %}
{% for tag in endpoint["tags"]%}
{% set tagValue = tag["tag"]["value"] %}
{% if tagValue not in tagValues %}
{% set tagValues = tagValues + [tagValue] %}
<span >{{ tagValue }}</span></a>
{% endif %}
{% endfor %}
{% endfor %}
it is not working, for example, for the enpoints list above, I am getting the following output:
car place van place
is there any problem with the codes ?
I recommend creating a distinct list of tags in your View. e.g.
distinctTags = list(set([tag for endpoint in endpoints for tag in endpoint]))
and passing that to your template
{% for tag in distinctTags %}
<span >{{ tagValue }}</span></a>
{% endfor %}
this has the advantage of the distinct tag code being reusable and the code being less procedural.
my jinja knowledge is limited, but by adding tagValues to the output, it appears that it's reset after each iteration of the outer loop. I'd guess it's to do with scopes, but don't know.
My recommendation would be to pre-process your endpoints in regular python before passing to jinja

sort in jinja2 when attribute is not available

I have a set of articles (using pelican for generating static sites) which includes a category called hotels. I'd like to sort these hotels. The problem is that only the hotels have an attribute called 'city' while the other articles do not and this obviously leads to the following error:
Caught exception "'pelican.contents.Article object' has no attribute 'city'".
Here is the code I am using:
{% for article in articles|sort(attribute='city') %}
{% if article.category == 'hotels' %}
<a href="hotels/{{ article.slug }}.html">
<p>{{ article.title }}</p>
</a>
{% endif %}
{% endfor %}
Is there a way to check to see if the attribute exists and provide some default value so that it does not cause an error?
You may be able to move your if statement into your for loop as a filter:
for article in articles if article.category == 'hotels' | sort(attribute='city')
If you want to show only entries that have a 'city' attribute, and have that list sorted by 'city', do:
for article in articles|selectattr("city")|sort(attribute="city")
If you want to iterate over only the hotels, see Sean Vieira's answer. If you want to iterate over all articles, but have the hotels sorted while the rest are in arbitrary order, you can do it by using macros:
{% macro my_macro(article) %}
...
{% endmacro %}
{% for a in articles if a.category == 'hotels' | sort(attribute='city') %}
{{ my_macro(a) }}
{% endfor %}
{% for a in articles if a.category != 'hotels' %}
{{ my_macro(a) }}
{% endfor %}
This will include everything you defined in my_macro first for each hotel, in the desired order, then for each article that is not a hotel.
I found this page when was looking for a similar solution.
Eventually, I solved it a bit differently and it might be helpful for someone else.
In one of my templates for Pelican I added statistics collected by 'post_stats' plugin about approximate time to read. It looked like
~{{ article.stats['read_mins']|default("0") }} min read
But if the plugin is not loaded then the 'article' object doesn't have the 'stats' attribute and rendering fails.
Jinja has the builtin test for testing if a variable is defined.
So, I came up with this solution
~{{ article.stats['read_mins'] if article.stats is defined else "0" }} min read

Inlines Python/Django technique for objects

I am reading the source code of the Django application blog at git://github.com/nathanborror/django-basic-apps.git.
How do you read the following Django code?
{% tags_for_object object as tag_list %}
My attempt: Make the variable object of the type tags_for_object and rename the variable to tag_list.
The object apparently is based on the file blog/templates/inlines/default.html:
{% if object %}
{{ object }}
{% else %}
{% for object in object_list %}
{{ object }}
{% endfor %}
{% endif %}
What is the befefit of putting the logic to two-step procedure: run single object, else loop through a list of objects?
It looks like tags_for_object is the template tag from the django-tagging application.
From the django-tagging documentation:
tags_for_object:
Retrieves a list of Tag objects
associated with an object and stores
them in a context variable.
Usage:
{% tags_for_object [object] as [varname] %}
Example:
{% tags_for_object foo_object as tag_list %}
You can then loop through the tag_list variable in the template to display the tags.
{% tags_for_object foo_object as tag_list %}
<ul>
{% for tag in tag_list %}
<li>{{ tag }}</li>
{% endfor %}
</ul>
For the second part of your question, you understand the code correctly. If the variable object exists in the context (and doesn't evaluate to False), it is displayed. If it does not exist in the context (or if it evaluates to False), then the code loops through the objects in object_list, and displays them.
As for why you would want to do this, you would have to look at the code that uses inlines/default.html to work out what the designer had in mind.

How do I access a python list from a django templatetag?

I have created a templatetag that loads a yaml document into a python list. In my template I have {% get_content_set %}, this dumps the raw list data. What I want to be able to do is something like
{% for items in get_content_list %}
<h2>{{items.title}}</h2>
{% endfor %}`
If the list is in a python variable X, then add it to the template context context['X'] = X and then you can do
{% for items in X %}
{{ items.title }}
{% endfor %}
A template tag is designed to render output, so won't provide an iterable list for you to use. But you don't need that as the normal context + for loop are fine.
Since writing complex templatetags is not an easy task (well documented though) i would take {% with %} tag source and adapt it for my needs, so it looks like
{% get_content_list as content %
{% for items in content %}
<h2>{{items.title}}</h2>
{% endfor %}`

Categories