python/django for loop and list attributes - python

So, i'm studying the Django Book, and django documentation, and I can't understand this example:
<ul>
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
</ul>
This is about templates and i don't how to code the Context. How can i get attribute called "name" from a list ? If i create a dictionary it will be impossible to use for loop like in this example. I have coded it like this but it's not working:
athlete_list = {'name' = ['Athlete1', 'Athlete2', 'Athlete3']}
Context({'athlete_list':athlete_list})
if i change athlete_list variable to a normal list (not a dictionary) the "athlete.name" in the template won't work too. I don't think it's a mistake in a book, and it's probably very easy to solve, but i can't get it.

I'd suspect that athlete_list is a QuerySet object containing Athlete models... (does that get mentioned anywhere?). The models will then have a .name or .age or .sport or whatever...
update - just looked at http://www.djangobook.com/en/2.0/chapter04.html - which actually doesn't appear to be the best example....
To keep the template as is, you can return a context of a list of dicts, eg:
[ {'name': 'bob'}, {'name': 'jim'}, {'name': 'joe'} ]

If you want to keep the template,you should return below.
athlete_list = ({'name':'Athlete1'},{'name':'Athlete2'},{'name':'Athlete3'})
Context({'athlete_list':athlete_list})

Your athlete_list is actually a dict
<ul>
{% for athlete_name in athlete_list.name %}
<li>{{ athlete_name }}</li>
{% endfor %}
</ul>
in templates you can access dictionary keys through . instead of through []
so in your template {{ athleate_list.name }}
would be a list of strings # ['Athlete1', 'Athlete2', 'Athlete3']

Related

How to loop through dictionary in jinja template

I have a dictionary that looks something like this:
{
'Team Starwars': {'Luke Skywalker': {('Jedi', 100)}}
'Team helloworld': {'Beginner': {('newbie', 100)}}
}
And now I want to iterate through the dictionary with jinja in a template.
I have tried somethings but can't iterate through it correctly.
The code I have now looks something like this:
{% for team_name in team_resource %}
{% for team, name in team_resource.items %}
{% for role, allocation in subrole %}
{% if forloop.counter0 != 0 %}<br>{% endif %}
{{role}} {{allocation}} %
{% endfor %}
{% endfor %}
team_resource is the dictionary that I pass to the template, and in the first loop I can access the first part of the dictionary and print out like Team Starwars and Team helloworld', but can't access the rest of the dict.
How should I do this?
You should use team_resource.items() instead of team_resource.items to access dict items.
What you refer to as 'the rest of the dict' is actually the value of the key you managed to retrieve successfully.
Without testing, I believe the variable name is the one that holds the {'Luke Skywalker': {('Jedi', 100)} part of the dictionary in your example.

key and value of dictionary in html file Django [duplicate]

I would like to print out the number of votes that each choice got. I have this code in a template:
{% for choice in choices %}
{{choice.choice}} - {{votes[choice.id]}} <br />
{% endfor %}
votes is just a dictionary while choices is a model object.
It raises an exception with this message:
"Could not parse the remainder"
choices = {'key1':'val1', 'key2':'val2'}
Here's the template:
<ul>
{% for key, value in choices.items %}
<li>{{key}} - {{value}}</li>
{% endfor %}
</ul>
Basically, .items is a Django keyword that splits a dictionary into a list of (key, value) pairs, much like the Python method .items(). This enables iteration over a dictionary in a Django template.
you can use the dot notation:
Dot lookups can be summarized like
this: when the template system
encounters a dot in a variable name,
it tries the following lookups, in
this order:
Dictionary lookup (e.g., foo["bar"])
Attribute lookup (e.g., foo.bar)
Method call (e.g., foo.bar())
List-index lookup (e.g., foo[2])
The system uses the first lookup type
that works. It’s short-circuit logic.
To echo / extend upon Jeff's comment, what I think you should aim for is simply a property in your Choice class that calculates the number of votes associated with that object:
class Choice(models.Model):
text = models.CharField(max_length=200)
def calculateVotes(self):
return Vote.objects.filter(choice=self).count()
votes = property(calculateVotes)
And then in your template, you can do:
{% for choice in choices %}
{{choice.choice}} - {{choice.votes}} <br />
{% endfor %}
The template tag, is IMHO a bit overkill for this solution, but it's not a terrible solution either. The goal of templates in Django is to insulate you from code in your templates and vice-versa.
I'd try the above method and see what SQL the ORM generates as I'm not sure off the top of my head if it will pre-cache the properties and just create a subselect for the property or if it will iteratively / on-demand run the query to calculate vote count. But if it generates atrocious queries, you could always populate the property in your view with data you've collected yourself.
You need to find (or define) a 'get' template tag, for example, here.
The tag definition:
#register.filter
def hash(h, key):
return h[key]
And it’s used like:
{% for o in objects %}
<li>{{ dictionary|hash:o.id }}</li>
{% endfor %}
django_template_filter
filter name get_value_from_dict
{{ your_dict|get_value_from_dict:your_key }}
Similar to the answer by #russian_spy :
<ul>
{% for choice in choices.items %}
<li>{{choice.0}} - {{choice.1}}</li>
{% endfor %}
</ul>
This might be suitable for breaking down more complex dictionaries.
Ideally, you would create a method on the choice object that found itself in votes, or create a relationship between the models. A template tag that performed the dictionary lookup would work, too.
Could find nothing simpler and better than this solution. Also see the doc.
#register.filter
def dictitem(dictionary, key):
return dictionary.get(key)
But there's a problem (also discussed here) that the returned item is an object and I need to reference a field of this object. Expressions like {{ (schema_dict|dictitem:schema_code).name }} are not supported, so the only solution I found was:
{% with schema=schema_dict|dictitem:schema_code %}
<p>Selected schema: {{ schema.name }}</p>
{% endwith %}
UPDATE:
#register.filter
def member(obj, name):
return getattr(obj, name, None)
So no need for a with tag:
{{ schema_dict|dictitem:schema_code|member:'name' }}
You could use a namedtuple instead of a dict. This is a shorthand for using a data class. Instead of
person = {'name': 'John', 'age': 14}
...do:
from collections import namedtuple
Person = namedtuple('person', ['name', 'age'])
p = Person(name='John', age=14)
p.name # 'John'
This is the same as writing a class that just holds data. In general I would avoid using dicts in django templates because they are awkward.

how to set custom forloop start point in django template

There is a forloop in java where i can tell where to start and where to end:
for(int i=10;i<array.length;i++){
}
but how can i implement this int i=10 in django template? How can i set the starting and ending point on my own?
there is a forloop.first and forloop.last, but they are defined inside the loop and i cannot do something like this?:
{{forloop.first=10}}
{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
{{forloop.last=20}}
I read the django doc but this feature seems to be not there
How about using built-in slice filter:
{% for athlete in athlete_list|slice:"10:20" %}
<li>{{ athlete.name }}</li>
{% endfor %}
If you need to make a numeric loop (just like python's range), you need a custom template tag, like this one: http://djangosnippets.org/snippets/1926/
See other range snippets:
http://djangosnippets.org/snippets/1357/
http://djangosnippets.org/snippets/2147/
Also see:
Numeric for loop in Django templates
By the way, this doesn't sound like a job for templates - consider passing a range from the view. And, FYI, there was a proposal to make such tag, but it was rejected because it is trying to lead to programming in the template. - think about it.

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

Django dictionary in templates: Grab key from another objects attribute

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.

Categories