Django: templates and iterate over dict - python

I have a dict:
>>> some_dict
{1: ['String1', 'String2', 'String3','String4' ],
2: ['String1_2', 'String2_2', 'String3_2', 'String4_2' ],}
In my template i want to iterate over this dict and display values in html. So i sent this dict from view:
return render_to_response('tournament.html',
{.....
'some_dict' : some_dict,
'some_dict_range' : range(4),
.....
})
In tournament.html i trying to iterate over some_dict. I want to get output that should looks like:
'String1', 'String2', 'String3','String4'
{% for iter1 in some_dict_range%}
{{some_dict.0.iter1}}<br>{% endfor %}
And as result, i get nothing.
But when i trying to get same result without iterator:
some_dict.0.0, some_dict.0.1, etc. i get what i need ('String1', 'String2', 'String3','String4').
And when i trying to view values of "iter1" i get the right digits:
{% for iter1 in some_dict_range%}
{{iter1}}<br> {% endfor %}
0, 1, 2 ...
Why this doesn't work this way? And if i wrong in design of this, how it should looks like? I mean - what the right way to iterate over this dict and display values in html-template?

Shouldn't:
{{some_dict.0.iter1}}<br>{% endfor %}
Be:
{{some_dict.iter1.0}}<br>{% endfor %}
^^^^^^^
Else you're trying to access some_dict[0] which isn't present...
To avoid passing in the range (as I assume you're wanting to output the dict in key order), you can use the following:
{% for k, v in some_dict.items|sort %}
Position {{ k }} has a first value of {{ v.0 }} and has:<br>
{{ v|join:"<br/>" }}
{% for item in v %}
{{ item }}
{% endfor %}
{% endfor %}

Related

Determining if I need quotes in HTML with django template

I have a dict in python that is something like the following:
d = {'key1': .98, 'key2': 'some_str',
#...
}
In this dictionary some keys will be mapped to float and others will be mapped to str
In my HTML I am doing something like the following:
html_dict = {
{% for k, v in my_dict.items %}
{{ k }}: "{{ v }}",
{% endfor %}
};
However this approach wraps the floats in quotes as well, which is what I don't want. But if I don't know wrap them in quotes the HTML doesn't understand that they are are string values. I would ideally like something like the following:
html_dict = {
{% for k, v in my_dict.items %}
{% if check_v_is_str %}
{{ k }}: "{{ v }}",
{% else %}
{{ k }}: {{ v }},
{% endif %}
{% endfor %}
};
Don't do this. You are manually trying to replicate a JS data structure, when there already exists a well-defined structure that both Python and JS know about, namely JSON. Use that instead. Convert the dict to JSON in your view, and pass it to the template where it can be output directly.
You could define a filter called "check_v_type" to call in your template as follows:
{% if v|check_v_type == 'str' %}
But I don't think that makes for good logic. Generally, you want to avoid function calls in your templates. Instead, in your view, you could create a dictionary of tuples of the form:
d = {'key1': (.98, False), 'key2': ('some_str', True), #...}
You could then use the boolean in each tuple to decide how to format the value in your HTML.

Loop through a defaultdict(list) structure in a django template

I have a structure my_dict like this:
defaultdict(<class 'list'>, {
<MyClass: myobject1>: [<ThingClass: mything1>, <ThingClass: mything2>, ...],
<MyClass: myobject2>: [<ThingClass: mything6>, <ThingClass: mything7>, ...],
<MyClass: myobject3>: [<ThingClass: mything45>, <ThingClass: mything46>, ...],
...
})
I want to loop through the objects something like this:
{% for object in my_dict %}
{{object.somefield}}
{% for thing in object %}
{{thing.somefield}}
{% endfor %}
{% endfor %}
How do I loop through the things in this nested loop? myobject1 is not iterable, so how do I get the iterable?
You should loop through .items() of the dictionary to get both object and the list in hand at each iteration:
{% for obj, things in my_dict.items %}
{{obj.somefield}}
{% for thing in things %}
{{thing.somefield}}
{% endfor %}
{% endfor %}

Django template and part of a dictionary of lists

in Django I want to display some entries in a dictionary of lists.
My context is:
keys = ['coins', 'colors']
dict = {'colors':['red', 'blue'],
'animals':['dog','cat','bird'],
'coins':['penny','nickel','dime','quarter'] }
Template code:
<ul>{% for k in keys %}
<li>{{ k }}, {{ dict.k|length }}: [{% for v in dict.k %} {{ v }}, {% endfor %}]
{% endfor %}</ul>
I want to see:
* coins,4: [penny, nickel, dime, quarter,]
* colors,2: [red, blue,]
But what I actually see is keys but no values:
* coins,0: []
* colors,0: []
Note: I also tried dict.{{k}} instead of dict.k, but as expected that just gave a parse error in template rendering. I'll get rid of the trailing comma with forloop.last after getting the basic list working.
What is the secret sauce for displaying selected values from a dictionary of lists?
The question django template and dictionary of lists displays an entire dictionary, but my requirement is to display only a few entries from a potentially very large dictionary.
The problem (as you suspected) is that dict.k is evaluated to dict['k'] where 'k' is not a valid key in the dictionary. Try instead to iterate each item pair using dict.items and only display the results for the keys you're concerned with:
<ul>{% for k, v in dict.items %}
{% if k in keys %}
<li>
{{ k }}, {{ v|length }}: [{% for val in v %} {{ val }},{% endfor %}]
</li>
{% endif %}
{% endfor %}
</ul>
<ul>
{% for k, v in dict.items %} # instead of iterating keys, iterate dict
{% if k in keys %} # if key found in keys
<li>
{{ k }}, {{ v|length }}: [{% for val in v %} {{ val }},{% endfor %}]
</li>
{% endif %}
{% endfor %}
</ul>

jinja2 recursive loop vs dictionary

I have the following dictionary:
{'a': {'b': {'c': {}}}}
And the following Jinja2 template:
{% for key in dictionary recursive %}
<li>{{ key }}
{% if dictionary[key] %}
<ul>{{ loop(dictionary[key]) }}</ul>
{% endif %}
</li>
{% endfor %}
But Jinja2 always output:
<ul>
<li>a</li>
<ul>
<li>b</li>
</ul>
</ul>
My understood is that using recursive, it would show me the "c" element too, but it only works for a depth of 2. Why is dictionary not changing to the dictionary[key] at every loop iteration ? The dictionary is always the original dictionary.
You're right, dictionary isn't being updated in the recursion calls, and the loop cannot continue because the keys aren't found.
A workaround to this problem is using just the variables assigned in the for loop. In the dictionary example, this means to iterate through the items of the dictionary instead of just the keys:
from jinja2 import Template
template = Template("""
{%- for key, value in dictionary.items() recursive %}
<li>{{ key }}
{%- if value %}
Recursive {{ key }}, {{value}}
<ul>{{ loop(value.items())}}</ul>
{%- endif %}
</li>
{%- endfor %}
""")
print template.render(dictionary={'a': {'b': {'c': {}}}})
The output of this script is:
<li>a
Recursive a, {'b': {'c': {}}}
<ul>
<li>b
Recursive b, {'c': {}}
<ul>
<li>c
</li></ul>
</li></ul>
</li>
where you can see that recursion on the b key works fine because both key and value are updated on each iteration of the loop (I added the "Recursive key, value" message to the template to make it clear).
try something like this:
{% for key in dictionary recursive %}
<li>{{ key }}
{% if dictionary[key] %}
<ul>{{ loop(dictionary[key].keys()) }}</ul>
{% endif %}
</li>
{% endfor %}
I think you need to pass an iterable into the loop() construct.

Django - How to do tuple unpacking in a template 'for' loop

In my views.py, I'm building a list of two-tuples, where the second item in the tuple is another list, like this:
[ Product_Type_1, [ product_1, product_2 ],
Product_Type_2, [ product_3, product_4 ]]
In plain old Python, I could iteration the list like this:
for product_type, products in list:
print product_type
for product in products:
print product
I can't seem to do the same thing in my Django template:
{% for product_type, products in product_list %}
print product_type
{% for product in products %}
print product
{% endfor %}
{% endfor %}
I get this error from Django:
Caught an exception while rendering: zip argument #2 must support iteration
Of course, there is some HTML markup in the template, not print statements. Is tuple unpacking not supported in the Django template language? Or am I going about this the wrong way? All I am trying to do is display a simple hierarchy of objects - there are several product types, each with several products (in models.py, Product has a foreign key to Product_type, a simple one-to-many relationship).
Obviously, I am quite new to Django, so any input would be appreciated.
Another way is as follows.
If one has a list of tuples say:
mylst = [(a, b, c), (x, y, z), (l, m, n)]
then one can unpack this list in the template file in the following manner.
In my case I had a list of tuples which contained the URL, title, and summary of a document.
{% for item in mylst %}
{{ item.0 }} {{ item.1}} {{ item.2 }}
{% endfor %}
it would be best if you construct your data like {note the '(' and ')' can be exchanged for '[' and ']' repectively, one being for tuples, one for lists}
[ (Product_Type_1, ( product_1, product_2 )),
(Product_Type_2, ( product_3, product_4 )) ]
and have the template do this:
{% for product_type, products in product_type_list %}
{{ product_type }}
{% for product in products %}
{{ product }}
{% endfor %}
{% endfor %}
the way tuples/lists are unpacked in for loops is based on the item returned by the list iterator.
each iteration only one item was returned. the first time around the loop, Product_Type_1, the second your list of products...
You must used this way:
{% for product_type, products in product_list.items %}
{{ product_type }}
{% for product in products %}
{{ product }}
{% endfor %}
{% endfor %}
Don't forget the variable items in the dictionary data
If you have a fixed number in your tuples, you could just use indexing. I needed to mix a dictionary and the values were tuples, so I did this:
In the view:
my_dict = {'parrot': ('dead', 'stone'), 'lumberjack': ('sleep_all_night', 'work_all_day')}
In the template:
<select>
{% for key, tuple in my_dict.items %}
<option value="{{ key }}" important-attr="{{ tuple.0 }}">{{ tuple.1 }}</option>
{% endfor %}
</select>
Just send the template a list of product types and do something like:
{% for product_type in product_type_list %}
{{ product_type }}
{% for product in product_type.products.all %}
{{ product }}
{% endfor %}
{% endfor %}
It's been a little while so I can't remember exactly what the syntax is, let me know if that works. Check the documentation.

Categories