How to iterate through multidimensional list/dict in a template - python

I try to rebuild this example:
https://blog.roseman.org.uk/2010/01/11/django-patterns-part-2-efficient-reverse-lookups/
I have a model "Product" and a model "Order". Order has a foreignkey to "product". So for 1 Product I have N Orders
In my template I have to display a lot of information so I would like to avoid to do "for order in Product.order_set.all()" in my template
In my template, if I write :
{{ object_list.1.related_items }}
everything is fine and I get what I want
but if I write:
{% for i in object_list %}
{{ object_list.i.related_items }}
{% endfor %}
I don't get a result.
Can somebody tell me how I solve this problem?
My object_list is nearly the same as in the above example:
products = Product.objects.all()
i = 0
qs = Product.objects.all()
obj_dict = dict([(obj.id, obj) for obj in qs])
objects = Order.objects.filter(producttyp__in=qs)
relation_dict = {}
for obj in objects:
relation_dict.setdefault(obj.producttyp_id, []).append(obj)
for id, related_items in relation_dict.items():
obj_dict[id].related_items = related_items
def get(self,request,*args,**kwargs):
context = {'object_list':self.obj_dict}
return render(request,self.template_name,context)
the only change i did is from
obj_dict[id]._related_items to obj_dict[id].related_items because of the not allowed underscore?!
How do I print the list in my template like:
- Product A
- Order 1
- Order 2
- Order 5
- Product B
- Order 3
- Order 6
best regards

That is logical, since here Django interprets i not as the variable, but as the an identifier, so it aims to access object_list.i, or object_list['i'], not object_list.1 for example.
You however do not need i here, you can just access the related_items of the object, like:
{% for object in object_list %}
{{ object.related_items }}
{% endfor %}
If related_items is, as the name suggests, a collection as well, we can iterate over these items as well:
{% for object in object_list %}
{% for subitem in object.related_items %}
{{ subitem }}
{% endfor %}
{% endfor %}
for a dictionary, we can access the .values, like:
{% for object in object_dict.values %}
{{ object.related_items }}
{% endfor %}
EDIT: as for the specific case of the listview. You can use .prefetch_related to fetch all the relations with one extra query:
class MyListView(ListView):
queryset = Product.objects.prefetch_related('order_set')
template = 'my_template.html'
In the template you can then render this like:
<ul>
{% for product in object_list %}
<li>{{ product }}</li>
<ul>
{% for order in product.order_set %}
<li>{{ order }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>

Related

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.

How to display calculated data in template

I need to get values from model fields, multiply and display the product in template.
For example, I have this code:
models.py
class Product(models.Model):
field1 = models.IntegerField()
field2 = models.IntegerField()
def multiply(self):
return self.field1 * self.field2
views.py
def home(request):
products = Product.objects.all()
#something goes here?
context = {
'products': products
}
return render(request, 'home.html', context)
template
{% for product in products %}
{{ product.field1 }}
{{ product.field2 }}
here goes the value of field1/field2 {{ }}
{% endfor %}
How it's better to achieve this?
You can add a method on the model:
def divided_fields(self):
return self.field1 / self.field2
And then im template:
{{ product.divided_fields }}
Another possibility would be to create a custom template tag or filter that handles division as there is no default tag or filter for such operations.
You can use django-mathfilters for this.
{% load mathfilters %}
...
<h1>Basic math filters</h1>
<ul>
<li>8 + 3 = {{ 8|add:3 }}</li>
<li>13 - 17 = {{ 13|sub:17 }}</li>
{% with answer=42 %}
<li>42 * 0.5 = {{ answer|mul:0.5 }}</li>
{% endwith %}
{% with numerator=12 denominator=3 %}
<li>12 / 3 = {{ numerator|div:denominator }}</li>
{% endwith %}
<li>|-13| = {{ -13|abs }}</li>
</ul>

How to tell list from non-list in Django template?

I've got a Django template I'd sometimes like to pass a list and sometimes like to pass a single value. How can the template tell which it was given?
I'm thinking the value would be set like one of these:
context = {
'foo' : 'bar
}
or:
context = {
'foo' : ['bar', 'bat', 'baz']
}
Then, the template would have code that looks something like this:
{% if foo isa list %}
{% for item in foo %}
{{ item }}<br>
{% endfor %}
{% else %}
{{ item}}<br>
{% endif %}
I can set it up to have foo or foolist, for example, and check for one or the other. However, it'd be a bit nicer (imo) to just have foo that was either a list or not.
If you intend to do it this way then just add a check that it doesn't have format(in case of string) method and has 0 index, if so then its a list else considered single value
{% if foo.0 and not foo.format %}
{% for item in foo %}
{{ item }}<br>
{% endfor %}
{% else %}
{{ item}}<br>
{% endif %}
I think your approach is needlessly complicated.
I would just go with a list:
views.py
foo_list = ['bar']
context = {
'foo': foo_list,
'foo_len': len(foo_list),
}
template
{% if foo_len == 1 %}
{{ foo.0 }}
{% else %}
{% for item in foo %}
{{ item }}
{% endfor %}
{% endif %}

Django - trouble with separating objects by user and date

So I have these models:
excercises_choices = (('Bench Press', 'Bench press'),('Overhead Press', 'Overhead Press'), ('Squat', 'Squat'),
('Deadlift', 'Deadlift'))
unit_choices = (('kg','kg'), ('lbs', 'lbs'))
class Lifts(models.Model):
user = models.ForeignKey('auth.User', null=True)
excercises = models.CharField(max_length=200, choices=excercises_choices)
sets = models.IntegerField(null=True, blank=True)
reps = models.IntegerField(null=True, blank=True)
weight = models.FloatField()
unit = models.CharField(max_length=3, choices=unit_choices)
created_date = models.ForeignKey('Dates')
amrap_set = models.BooleanField(default=False)
amrap_rep = models.IntegerField(null=True, blank=True)
def __str__(self):
return self.excercises
class Dates(models.Model):
created_date = models.DateField(unique=True)
def __str__(self):
return str(self.created_date)
Let's say I have few lifts at different dates for admin and few lifts at different for xx user.
I want multiple lifts matching one date that's why I've made foreign key. (eg. 3 lifts to 2016-10-10 and 2 lifts to 2016-10-11).
Here is a view for showing it:
#login_required
def entries(request):
date = Dates.objects.all().order_by('-created_date')
lifts_by_user = Lifts.objects.filter(user=request.user)
return render(request, 'lift/entries.html', {'date': date,
'lifts_by_user': lifts_by_user})
And template:
{% extends 'lift/base.html' %}
{% block content %}
{{ user }}
{% if user.is_authenticated %}
{% for date in date %}
<p><strong>{{ date }}</strong>
{% for i in date.lifts_set.all %}
{{ i }}
{% endfor %}
add new lift</p>
{% endfor %}
{% endif %}
<p>
Delete lifts or dates
</p>
{% endblock %}
The problem is that I dont know how to separate it by dates AND by user.
This is how it looks like How do i keep this pattern date - lifts_to_that_date but for separate users? I dont want to see admin's entries while I am on test user
Have a look at the regroup template tag, it does exactly what you need.
You can do something like this in your view:
#login_required
def entries(request):
lifts_by_user = (Lifts.objects.filter(user=request.user)
.order_by('-created_date__created_date'))
return render(
request,
'lift/entries.html',
{'lifts_by_user': lifts_by_user}
)
And replace the for date in dates loop in your template with something like:
{% regroup lifts_by_user by created_date.created_date as lifts %}
<ul>
{% for day in lifts %}
<li>Date: {{ day.grouper }}
<ul>
{% for lift in day.list %}
<li>{{ lift }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
I've used a ul here so that it's easier to compare to the example in the docs, but obviously you can change the markup to whatever you need. It's important to know that regroup doesn't order its input, so you need to order by created_date in your view.
If you're using Django's dev version you can use this instead:
{% regroup lifts_by_user by created_date.created_date as lift_list %}
<ul>
{% for day, lifts in lift_list %}
<li>Date: {{ day }}
<ul>
{% for lift in lifts %}
<li>{{ lift }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Which I think is a little clearer.
As an aside, none of this relies on having dates stored as a foreign key, but that's up to you.
Questions from comments:
order_by('-created_date__created_date') is joining Lifts to Dates through the Lifts.created_date foreign key and ordering by the Dates.created_date field. Have a look at https://docs.djangoproject.com/en/dev/topics/db/queries/#lookups-that-span-relationships for details.
for day, lifts in lift_list is using tuple unpacking.
As a quick example:
t = (1, 2, 3)
# first, second, third will have values 1, 2, 3 respectively
first, second, third = t
{% regroup lifts_by_user by created_date.created_date as lifts_list %} produces a list of namedtuples (again, only in the dev version, if you're using 1.10 or earlier it's a list of dicts so you can't use this trick) so as you're iterating through lift_list you can unpack the date and list of lifts into separate variables.
If you have a Lift instance called lift, you can get the pk for its date by using lift.created_date_id. Accessing it where you have the date URL in your example template is a little trickier because you have to get a lift out of the regrouped date's list. Something like this:
{% regroup lifts_by_user by created_date.created_date as lifts %}
<ul>
{% for day in lifts %}
<li>Date: {{ day.grouper }}
{# day.list.0 gets the first lift for this day #}
Date PK: {{ day.list.0.created_date_id }}
<ul>
{% for lift in day.list %}
<li>{{ lift }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>

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