Can't iterate over nestled dict in django - python

Im trying to iterate over a nestled dict list. The first level works fine. But the second level is treated like a string not dict.
In my template I have this:
{% for product in Products %}
<li>
<p>{{ product }}</p>
{% for partType in product.parts %}
<p>{{ partType }}</p>
{% for part in partType %}
<p>{{ part }}</p>
{% endfor %}
{% endfor %}
</li>
{% endfor %}
It's the {{ part }} that just list 1 char at the time based on partType. And it seams that it's treated like a string. I can however via dot notation reach all dict but not with a for loop. The current output looks like this:
Color
C
o
l
o
r
Style
S
.....
The Products object looks like this in the log:
[{'product': <models.Products.Product object at 0x1076ac9d0>, 'parts': {u'Color': {'default': u'Red', 'optional': [u'Red', u'Blue']}, u'Style': {'default': u'Nice', 'optional': [u'Nice']}, u'Size': {'default': u'8', 'optional': [u'8', u'8.5']}}}]
What I trying to do is to pair together a dict/list for a product from a number of different SQL queries.
The web handler looks like this:
typeData = Products.ProductPartTypes.all()
productData = Products.Product.all()
langCode = 'en'
productList = []
for product in productData:
typeDict = {}
productDict = {}
for type in typeData:
typeDict[type.typeId] = { 'default' : '', 'optional' : [] }
productDict['product'] = product
productDict['parts'] = typeDict
defaultPartsData = Products.ProductParts.gql('WHERE __key__ IN :key', key = product.defaultParts)
optionalPartsData = Products.ProductParts.gql('WHERE __key__ IN :key', key = product.optionalParts)
for defaultPart in defaultPartsData:
label = Products.ProductPartLabels.gql('WHERE __key__ IN :key AND partLangCode = :langCode', key = defaultPart.partLabelList, langCode = langCode).get()
productDict['parts'][defaultPart.type.typeId]['default'] = label.partLangLabel
for optionalPart in optionalPartsData:
label = Products.ProductPartLabels.gql('WHERE __key__ IN :key AND partLangCode = :langCode', key = optionalPart.partLabelList, langCode = langCode).get()
productDict['parts'][optionalPart.type.typeId]['optional'].append(label.partLangLabel)
productList.append(productDict)
logging.info(productList)
templateData = { 'Languages' : Settings.Languges.all().order('langCode'), 'ProductPartTypes' : typeData, 'Products' : productList }
I've tried making the dict in a number of different ways. Like first making a list, then a dict, used tulpes anything I could think of.
Any help is welcome!
Bouns: If someone have an other approach to the SQL quires, that is more then welcome. I feel that it kinda stupid to run that amount of quires. What is happening that each product part has a different label base on langCode.
..fredrik

Iterating over a dict yields the keys. You want either the iteritems() or itervalues() method.
{% for partName, partType in product.parts.iteritems %}
<p>{{ partName }}</p>
{% for part in partType %}
<p>{{ part }}</p>
{% endfor %}
....

Related

How deep can a variable passthrough to jinja go with a join?

Working on a project -flask/sqlalchemy/jinja. If I were to call a join, loop through the set acquire the data through a variable could I call upon that variable multiple layers deep? Example:
orders = Orders.query.join(Products, Orders.id==Products.order_id).all()
Loop through records and find the values:
data = []
for order in orders:
object= {
'id': order.id,
'products': []
}
for product in order.products:
product = {
'name': product.name,
'header':product.header
}
object['products'].append(product)
data.append(object)
Render template passing data as data.
return render_template('something.jinja2', data=data)
In jinja can I do this?:
{% for d in data %}
{{order.products.name}}
{% endfor %}
If I were to do more joins and more loops could I go deeper into relationships?
loop in a loop in jinja
{% for d in data%}
{{ order.id}}
{% for product in d.products %}
{{product.name}}
{% endfor %}
{% endfor%}

django dictionary of querysets

i made i dictionary of querysets in one of my views :
list =[]
while(x<100 and i>0):
order_qs_1 = Order.objects.filter(timestamp__range=[start_date, end_date])
x = x+1
i=i-j
list.append(order_qs_1)
context= {
'list':list,
}
but now i don't know how to access the data inside the dictionary in my template
in my template i did something like this :
{% for item in list %}
{{ item }} </br>
{% endfor %}
this is what it renders:
the random numbers and characters are the order_id in my order model
the queryet is a list of orders coming from my order model
but this not what i want , i want the access the data inside each queryset
You can iterate over the querysets. For example:
{% for qs in list %}
<p>
{% for order in qs %}
{{ order }}
{% endfor %}
</p>
{% endfor %}
Here we thus iterate over the querysets qs, and for example render the order. You can thus render individual data of that order, for example {{ order.pk }} for the primary key.

Django, how to iterate over one to many fields of different lengths

I pretty new to Django and I am trying to create an app to display tennis match results. So far, beyond my belief I have managed to hack together some code that is working.
However, I now have a problem though as currently I want to list the matches and their scores together in a template, but the number of sets can be different for each match and when I iterate through them I am getting an index error. Some matches may have 2 sets, others 3,4 or 5... perhaps some even just 1 if a player retires.
I have models for the match and each set, something like this. (I could get the match date, match, result and Set1 etc. for the player to display as those lists all have the same number of values. However, the length of list for set3 as an example is much shorter and causes an error.):
models.py
class Match(models.Model):
match_ID = models.AutoField(primary_key=True)
match_date = models.DateField()
players = models.ManyToManyField(Team, through='MatchStats', related_name='pim')
hometeam = models.ForeignKey(Team, to_field='teamname', related_name='hometeam')
awayteam = models.ForeignKey(Team, to_field='teamname', related_name='awayteam')
hometeam_sets = models.IntegerField()
awayteam_sets = models.IntegerField()
class Set(models.Model):
set_ID = models.AutoField(primary_key=True)
match = models.ForeignKey(Match)
set_number = models.IntegerField()
hometeam_games = models.IntegerField(default=0)
awayteam_games = models.IntegerField(default=0)
views.py
def playermatches(request, player_ID):
context = RequestContext(request)
p = get_object_or_404(Player, pk=player_ID)
match_list = Match.objects.filter(players=player_ID).order_by('-match_date')[:100]
i = len(match_list)
j = 0
#This works as all test matches have a 1st set!
s1_list = Set.objects.filter(match=match_list, set_number=1).order_by(-match__match_date')[:100]
"""
I am totally out of ideas as to what I might do next though.
Tried various things like 'if exists', 'try except IndexError etc'.
Below was the last thing I tried which failed yet again.
"""
s3_list = []
while j < i:
s3 = Set.objects.filter(match=match_list, set_number=3)[j]
if s3:
s3_list.append(s2)
else:
s3 = Set.objects.filter(set_ID=1)
s3_list.append(s3)
lst1 = match_list
lst2 = result_list
lst3 = s1_list
...
lst5 = s3_list
mandr = zip(lst1, lst2, lst3,... lst5)
context_dict = {...}
return render_to_response('stats/players.html', context_dict, context)
template.html
{% if matches %}
<ul>
{% for match, team, s1, s2 in mandr %}
<li>{{ match.match_date }} {{ match.hometeam }} vs. {{ match.awayteam }} ( {{ team.result }} )</li>
<li>{{ match.hometeam_sets }}:{{ match.awayteam_sets }} -- {{ s1.hometeam_games }}-{{ s1.awayteam_games }} {{ s3.hometeam_games }}-{{ s3.awayteam_games }}</li>
{% endfor %}
</ul>
{% else %}
<br/>
<strong>There are currently no matches in the system - try again tomorrow!</strong>
{% endif %}
Didn't need anything too fancy in the end. Realized that I need to lookup the related sets via the foreign key for each match... and then simply iterate over those in the template.
{% for match in mandr %}
{% for set in match.sets.all %}
{{ set.hometeam_games }}:{{ set.awayteam_games }}
{% endfor %}
{% endfor %}
Don't mix match list, result list with remaining lists. Pass them as separate objects in views and iterate over them in templates.
Change your views & template as follows.
views.py
m_list = match_list
r_list = result_list
list1 = s1_list
list2 = s2_list
..............
list5 = s3_list
mandr = zip(m_list, r_list)
my_lists = zip(list1, list2, ..., lst5)
templates.py
{% if matches %}
<ul>
{% for match, team in mandr %}
<li>{{ match.match_date }} <a href="/stats/match/{{ match.match_ID }}/">
{{ match.hometeam }} vs. {{ match.awayteam }}</a> ( {{ team.result }} )</li>
<li>{{ match.hometeam_sets }}:{{ match.awayteam_sets }} --
{% for lst in my_lists %}
<li>{{ lst.hometeam_games }}</li>
{% endfor %}
{% endfor %}
</ul>
{% else %}
<br/>
<strong>There are currently no matches in the system - try again tomorrow!</strong>
{% endif %}

Django: templates and iterate over dict

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 %}

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