django template and dictionary of lists - django

I'm using django's template system, and I'm having the following problem:
I pass a dictionary object, example_dictionary, to the template:
example_dictionary = {key1 : [value11,value12]}
and I want to do the following:
{% for key in example_dictionary %}
// stuff here (1)
{% for value in example_dictionary.key %}
// more stuff here (2)
{% endfor %}
{% endfor %}
However, this does not enter on the second for loop.
Indeed, if I put
{{ key }}
on the (1), it shows the correct key, however,
{{ example_dictionary.key }}
shows nothing.
In this answer, someone proposed using
{% for key, value in example_dictionary.items %}
However, this does not work in this case because I want (1) to have information regarding the particular key.
How do I achieve this? Am I missing something?

I supose that you are looking for a nested loop. In external loop you do something with dictionary key and, in nested loop, you iterate over iterable dictionary value, a list in your case.
In this case, this is the control flow that you need:
{% for key, value_list in example_dictionary.items %}
# stuff here (1)
{% for value in value_list %}
# more stuff here (2)
{% endfor %}
{% endfor %}
A sample:
#view to template ctx:
example_dictionary = {'a' : [1,2]}
#template:
{% for key, value_list in example_dictionary.items %}
The key is {{key}}
{% for value in value_list %}
The key is {{key}} and the value is {{value}}
{% endfor %}
{% endfor %}
Results will be:
The key is a
The key is a and the value is 1
The key is a and the value is 2
If this is not that you are looking for, please, use a sample to ilustrate your needs.

Related

expected token 'end of statement block', got '='

I am trying to assign value to a dictionary in Jinja2 and its not working properly and showing error.
expected token 'end of statement block', got '='
My Code:
{% set sequence = ['a1', 'b1']%}
{% set dic = {} %}
{% for filter in search_result.filters %}
{% for seq_key in sequence %}
{% if seq_key == filter.key %}
{# here i wish to create a dictionary where key= seq_key and value = filter_object#}
{% do dic[seq_key]=filter %}
{% endif %}
{% endfor %}
{% endfor %}
The do statement takes only an expression, not a statement like an assignment. Since Jinja2 does not directly support assigning to a dict by key, you can call the dict's update method with a singleton dict containing the new key-value mapping instead.
Change:
{% do dic[seq_key]=filter %}
to:
{% do dic.update({seq_key: filter}) %}

How to acess form based on index django

I am trying to access the form based on the index value how can i do that exactly?
Ex:
{% for line in data_lines %}
{{line}}
{% with x=forloop.counter %}
{{form.x}}
{% endwith %}
{% endfor %}
As Django discourages adding too much logic hence I don't think it's possible using built-in Django Template features. You can achieve this by sending a dict instead of data_lines and accessing via key, value as:
{% for key, value in data_lines.items %}
{{value}}
{{ form.key }}
{% endfor %}
If you really want it then you could write a custom template filter.

Django check form error code in template

I am using Django 1.10 and trying to find a way to check the error code of a form inside of the template. So I tried a few things, like errors.as_data or errors.as_json, but I was unable to parse the different values (except by using javascript). Could it be something like this ?
<p>{% for key, value in form.errors.items %}
{{ value }}
{% if code == 'inactive_account'%}
// do some stuff
{% endif %}
{% endfor %}
</p>
But I don't know how to get this error code. Any suggestion ?
PS : I know that a solution would be to do it inside of the view, but since I am using a django already-made one, I would prefer not to do it.
The dictionary form.errors does not contain the ValidationError instances. You need to use the as_data method.
Note that you need to loop through the list of errors for each key, and then you can check the code.
{% for key, key_errors in form.errors.as_data.items %}
{{ key }}
{% for error in key_errors %}
{% if error.code == 'inactive_account'%}
// do some stuff
{% endif %}
{% endfor %}
{% endfor %}
There's also other way to check for the error code.
You can use has_error to check for the error code.
{% form.has_error 'field_name' 'code' %}
To check for non-field errors use NON_FIELD_ERRORS as the field parameter.

Determine order of iteration in django template for loop

I am populating a list in my view:
hits_object = {}
hits_object['Studio'] = hitlist('Studio',days)
hits_object['Film'] = hitlist('Film',days)
hits_object['Actor'] = hitlist('Actor',days)
hits_object['Historical Event'] = hitlist('Event',days)
hits_object['Pop Culture'] = hitlist('Pop_Culture',days)
Then I am displaying it in my template:
{% for model, hits in hits_object.items %}
{% if hits %}
<u> Most {{ model }} views in last {{ days }} days</u>
<ol>
{% for hit in hits %}
<li>{{ hit.name }} - {{ hit.count }}</li>
{% endfor %}
</ol>
</u>
{% endif %}
{% endfor %}
The problem is that the models display in a seemingly random order: first Actor, then Studio, Historical Event, Film, etc.
How can I force the for loop in the template to iterate the object in a specific order?
Dictionaries are unordered. If you need to preserve insertion order, use an ordered dict implementation - there's one in django.utils.datastructures.SortedDict, for example.
Or, since you don't seem to be using the key of the dictionary but are just iterating through, appending to a simple list would seem to be easier.
As Daniel explained, dictionaries are accessed randomly. Here is one way to do what you want:
hits_object = list()
hits_objects.append(
(hitlist('Studio',days),
hitlist('Film',days),
hitlist('Actor',days),
hitlist('Event',days),
hitlist('Pop_Culture',days))
In your view:
{% for studio,film,actor,event,pop_culture in hits_objects %}
# do something...
{% endfor %}

Django template for loop. Member before

I want to create such loop:
{% for object in objects %}
{% if object.before != object %}
{{ object }} this is different
{% else %}
{{ object }} this is the same
{% endfor %}
Based on https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#for I can't. Is there really no simple way to do this? Or I just need to use counter and check for objects[counter-1]?
P.S. .before is theoretical and objects is simple query list. I want to take and do something with the loop member that encountered before current loop member.
Check ifchanged template tag
There is a "simple way" to do this: write a custom template tag. They're really not hard. This would probably do the trick (untested):
#register.simple_tag
def compare_objects(object_list):
comparisons = []
for i in range(1, len(object_list)):
if object_list[i] > object_list[i-1]:
comparisons.append('bigger')
else:
comparisons.append('smaller')
return comparisons
The built-in template tags and filters don't make it easy (as of Django 1.4), but it is possible by using the with tag to cache variables and the add, slugify, and slice filters to generate a new list with only one member.
The following example creates a new list whose sole member is the previous member of the forloop:
{% for item in list %}
{% if not forloop.first %}
{% with forloop.counter0|add:"-1" as previous %}
{% with previous|slugify|add:":"|add:previous as subset %}
{% with list|slice:subset as sublist %}
<p>Current item: {{ item }}</p>
<p>Previous item: {{ sublist.0 }}</p>
{% endwith %}
{% endwith %}
{% endwith %}
{% endif %}
{% endfor %}
This isn't an elegant solution, but the django template system has two faults that make this hack unavoidable for those who don't what to write custom tags:
Django template syntax does not allow nested curly parenthesis. Otherwise, we could do this:
{{ list.{{ forloop.counter|add:-1 }} }}
The lookup operator does not accept values stored using with (and perhaps for good reason)
{% with forloop.counter|add:-1 as index %}
{{ list.index }}
{% endwith %}
This code should work just fine as a django template, as long as object has a property or no-argument method called before, and objects is iterable (and '<' is defined).
{% for object in objects %}
{% if object.before < object %}
this is bigger
{% else %}
this is smaller
{% endfor %}