3👍
You should avoid doing anything else than presentation in the templates. My suggestion is that you build a few convenient data structures in your view and just iterate over them in the template:
- a
list
of “Persons” (to create the table header) - a
dictionary
of items, where the item is the key (you can use the string representation of the item to keep things simpler) and the value is an ordered list of items in the same order the list at the previous point is ordered; you could also append the total to this list.
This makes it easier to work with things in the template, you’d have something like:
<table>
<thead>
<tr>
<td>Item name</td>
{% for person in person_list %}
<td>{{ person.name }}</td>
{% endfor %}
<td>Total</td>
</tr>
</thead>
<tbody>
{% for item, item_list in item_dict.items %}
<tr>
<td>{{ item }}</td>
{% for i in item_list %}
<td>{{ i }}</td>
{% endfor %}
</tr>
{% endfor %}
<tbody>
</table>
In order to integrate your categories, the dictionary
structure can be further nested in a category dictionary
that has data (the dict above) and also a list of totals.
Bottom line to what I’m suggesting is that you should build all your structures inside your view in such a way that displaying them is a breeze in the template, and not the other way around
0👍
I made a working example as per your models. Here are excerpts with explanations.
This is what your view would look like:
from django.shortcuts import render
from .models import *
def index(request):
all_selections = Selection.objects.all()
all_people = set([selection.person for selection in all_selections])
blank_person_counts = {person: 0 for person in all_people}
all_categories = set([selection.item.category for selection in all_selections])
categories = []
for category in all_categories:
selections = [s for s in all_selections if s.item.category == category]
total_by_person = blank_person_counts.copy()
unique_item_details = set([s.item for s in selections])
items = [{'detail': item_details} for item_details in unique_item_details]
total_by_item = {item['detail']: 0 for item in items}
total_by_item_person = {item['detail']: blank_person_counts.copy() for item in items}
for selection in selections:
total_by_item_person[selection.item][selection.person] = selection.quantity
total_by_item[selection.item] += selection.quantity
total_by_person[selection.person] += selection.quantity
for item in items:
item['people_totals'] = [total_by_item_person[item['detail']][person] for person in all_people]
item['total'] = total_by_item[item['detail']]
people_totals = [total_by_person[person] for person in all_people]
category_total = sum(people_totals)
categories.append({
'detail': category.name,
'people_totals': people_totals,
'total': category_total,
'items': items
})
context = {'people': all_people, 'categories': categories}
return render(request, 'matrix.html', context)
I started by getting all_selections
because I assumed if an item was never selected or if a person never selected a category, they shouldn’t be included, so the selections are the core of the data.
From the obtained selections, I generated a list of people (all_people
). Having this defined before we go into items helps us make sure that every person is accounted for in each item. Even if a person didn’t select an item, we will assign them a quantity of zero for that item. It also sets the order in which people are displayed as columns and we will use the same order when returning totals by people for each item and for each category.
Since the data is divided by category, we generate a list of categories from the list of selections. We then loop over each category, filter selections by that category, create a blank slate in which each item was selected by each person zero times. When then go over the actual selections and update the counts with the quantities. We also maintain running totals per person per category. In the end, we sum up the running totals per person as that will be the grand total for the category. We repeat the same process for each category.
In the template, it’s basically a matter of looping over this data and displaying it:
<h1>The Grocery Matrix</h1>
<table>
<tr>
<th></th>
{% for person in people %}
<th>{{ person }}</th>
{% endfor %}
<th>Total</th>
</tr>
{% for category in categories %}
{% for item in category.items %}
<tr>
<td>{{ item.detail }}</td>
{% for person_total_for_item in item.people_totals %}
<td>{{ person_total_for_item }}</td>
{% endfor %}
<td>{{ item.total }}</td>
</tr>
{% endfor %}
<tr>
<td>{{ category.detail }}</td>
{% for person_total_for_category in category.people_totals %}
<td>{{ person_total_for_category }}</td>
{% endfor %}
<td>{{ category.total }}</td>
</tr>
{% endfor %}
</table>
</body>
</html>
- [Django]-Passing Data from post() to get_context_data()
- [Django]-Get distinct django objects from queryset sorted by latest
- [Django]-Debugging Django Forms validation errors
- [Django]-Debugging dockerized Django in VS Code results in error "Timed out waiting for launcher to connect"
- [Django]-Django python-magic identify ppt,docx,word uploaded file as 'application/zip'