I'm getting a little stuck on a Django problem where I can't access the values of a dict in a for loop. It works outside the for loop, just not inside.
Am I missing the obvious here?
Python:
err{}
err['else'] = {'class': 'Low', 'txt': 'zero'}
err['if'] = {'class': 'High', 'txt': 'one'}
data = { 'errors': err }
return render(request, 'index/error.html', data)
HTML template:
<p>{{ errors }}</p>
<p>{{ errors.if }}</p>
<p>{{ errors.if.class }}</p>
{% for error in errors %}
<div class="{{ error.class }}"><p>{{ error.txt }}</p></div>
{% endfor %}
The upper 3 lines are for code debugging and work just fine.
The for loop doesn't produce any code.
Best regards,
LVX
You probably need to access .items() of the dict that you called errors. Just iterating over a dict gives you the keys, but not the values.
You can change your code to:
{% for k, v in errors.items %}
<div class="{{ v.class }}"><p>{{ v.txt }}</p></div>
{% endfor %}
Of course, if you don't need the keys (if and else) then you could also use .values() instead of items() to just get the values inside the dict.
The answer by Ralf is sufficient for the question, I just want to add an extra piece of information here.
When the template system encounters a dot in a variable name, it tries the following look-ups, in this order:
Dictionary Lookup (eg: foo['bar'])
Attribute Lookup (eg: foo.bar)
Method Call (eg: foo.bar())
List-Index Lookup (eg: foo[2])
The system uses the first lookup type that works.
You should try like this -
error['class']
Second way -
error[key]['class']
Use forloop -
for k,v in errors:
print(v['class'])
Related
I have a dictionary passed (as part of another object) to the django template language.
The object, called 'poll' has attributes self.text and self.votes, where the former is a string, and the latter is a dict.
The dict, looks like this:
{'a1': 45.92422502870264, 'a2': 53.50172215843857}
I am trying to list each label, with its accompanying number, using the following:
{% for l, x in poll.votes %}
<p>{{ l }} {{ x }}</p>
{% endfor %}
Django responds with
Exception Type: ValueError
Exception Value: Need 2 values to unpack in for loop; got 3.
I tried .iteritems - The docs explain that .iteritems is not the correct way to do this, but they don't explain what the correct way is.
You just iterate the same way you would in python, but in Djangos templating language (DTL) syntax
{% for key, value in dictionary.items %}
Your poll.votes is a dict but you're not iterating the items but the keys in your code.
You can find an overview of jinja here. Its worth noting that jinja isn't what django uses but its handy for a condensed reference since many things are the same (jinja is based upon DTL) instead of digging through djangos docs.
For Djangos tempaltes heres the documentation reference
you too can do this:
{% for variable in poll %}
{{ variable.name }} - {{ variable.votes }}
{% endfor %}
I have a dictionary passed to an html django template:
return render_to_response('showdata.html', context_instance=RequestContext(request, {'dictdati': context} ))
this dictionary has this structure:
{0: 'TO;DEC;1;2012/02/28 15:39:06.000;TO;1234;SI;DI;1234;TO;1\n', 1: 'TO;DEC;1;2012/02/28 15:39:06.000;TO;567;SI;DI;567;TO;1\n'}
and in an html template I need to print some of the values in the each row of the dict. If in a python file I use
for key,val in dictdati.items():
print val.split(';')[0]
it prints the first value correctly (TO) but in an html template
{% for key,val in dictdati.items %}
{{ val.split[0] }}
{% endfor %}
in the browser I receive the error:
TemplateSyntaxError at /getdata/
Could not parse the remainder: '[0]' from 'val.split[0]'
Can someone give me an idea to solve this problem?
you cant just call arbitrary logic in django... this is probably more of a job for the backend if you only want 0th but i think you can use the first filter
{{ my_var.split | first }}
I think I found solution so I made a custom Django filter
The Custom filter code:
from django import template
register = template.Library()
#register.filter
def get_index(value, arg):
return value[int(arg)]
And the HTML code:
{% for key,val in dict.items %}
{{ val|get_index:"0" }}
{% endfor %}
In get_index give the index number needed
Also, I see you have used string and then commas as values I have used it as a list so this code only works for value as list
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.
I am not able to append add a new entry into a dictionary object while using jinja2 template.
For example, here I am using jinja2 template and I have created a data variable which is a dictionary. And after checking some if condition I WANT to append location attribute to the data object e.g.
{%- set data = {
'name' : node.Name,
'id' : node.id,
}
-%}
{% if node.location !="" %}
data.append({'location': node.location})
{% endif %}
However I could not find a way to achieve this and am getting the UndefinedError:
jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'append'
Has anyone faced this issue or could provide a reference to solve this?
I searched the web but could not find a solution i.e. how to achieve adding an entry to the dict object in the Jinja.
I have referred following and other web resources:
http://cewing.github.io/training.codefellows/assignments/day22/jinja2_walkthrough.html
In Jinja2 whats the easiest way to set all the keys to be the values of a dictionary?
https://github.com/saltstack/salt/issues/27494
Without the jinja2.ext.do extension, you can do this:
{% set x=my_dict.__setitem__("key", "value") %}
Disregard the x variable and use the dictionary which is now updated.
UPD: Also, this works for len() (__len__()), str() (__str__()), repr() (__repr__()) and many similar things.
Dictionaries do not have the append method. You can add a key-value pair like this though:
{% do data['location']=node.location %}
or
{% do data.update({'location': node.location}) %}
Without the do extension:
{%- set _ = dict.update({c.name: c}) -%}
Works in base Jinja2 on Python 3, where the __setitem__ solutions give me:
access to attribute '__setitem__' of 'dict' object is unsafe
Key takeaways:
dictionary does not support append().
You can add the new item to the data dictionary by using {% do ... %} tag as shown here:
{% do data.update({'location': node.location}) %}
However, for the "do" tag to work properly you need to add the jinja2.ext.do extension to your jinja Environment.
Without any of the extensions:
{% set d1 = {'a':'b', 'c':'d'} %}
{% set d2 = {'e':'f'} %}
{{ dict(d1, **d2) }}
Basically you merge the two dicts to create a new one.
As this doesn't use any extensions, it also works in Home Assistant.
Is there any get() function for this instead?
{% for key, value in choices.items %}
<li>{{key}} - {{value}}</li>
{% endfor %}
From python I have the get() function to get values from a specific key. But I couldn't find a corresponding way to do that with django template tags. So I wonder is it possible?
I need to get specific values since using loops adds a lot of new lines in the html source.
Or should take care of the output inside the view before sending it out to the template, which method is better?
You can use {{ choices.items.key }} to access a specific dict element.
There is no reason to care about whitespace in the HTML code though; the typical end-user has no real business in reading it and if he's curious he an always use a DOM viewer or run it through a HTML beautifier.
If you want a specific value, just add it to the dotted-path:
{{ choices.items.somekey }}
will get you the value of choices.items['somekey'] if choices.items is a dict.
I think you are way advance now, just to share my point. you could do this way as well
{% for value in dict %}
{{value}}
{% endfor %}
or with key, value like
{% for key,value in dict.items %}
{{key}} : {{ value }}
{% endfor %}
If choices type is DICT like {}.
{{choices.somekey|default:""}}
If choices.items is DICT type.
{{choices.items.somekey|default:""}}
Try See little example.
# In Views.py
def dict_test(request):
my_little_dict = {"hi": "Hello"}
....
# in Template
{{my_little_dict.hi}}
You can specify as {{ choices.key_name }}
It worked for me. Just simple
{{ choices.values }}
This gives you a list of all the values in the dictionary at once.