Pattern to organize related items into groups - python

Let me try to explain what I want to achieve by a small example application. It is a shopping list application that lets you add predefined Items to your ShoppingList as Groceries. The smart thing would be that every Item is related to a Store.
When I am done adding groceries to my shopping list, I should be able to see my shopping list with the groceries organized (grouped) by their Store. Something like:
How would I create a view and template to group and display this shopping list?
For example: this is how I would display the full inventory of all stores:
#views.py
store_list = Store.objects.all()
#template.html
{% for store in store_list %}
<div>{{store}}
<ul>
{% for item in store.item_set.all %}
<li>{{item}}</li>
{% endfor %}
</ul>
</div>
{% endfor %}
That won't work for my shopping list, but I am really looking for something equally powerful / elegant....

What about the built in regroup template tag?
{% regroup item.objects.all by store as store_groups %}
<ul>
{% for store_group in store_groups %}
<li>{{ store_group.grouper }}
<ul>
{% for item in store_group.list %}
<li>{{ item }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>

Do the grouping in the backend of the view instead of the template.
In your view, create a dictionary like this:
sorted_groceries = {
store1 : [item1, item2],
store2 : [item3, item4],
}
which can be done with some code resembling this pseudo-code:
sorted_groceries = {}
for item in shopping cart:
try:
sorted_groceries[item.store].append(item)
except KeyError:
sorted_groceries[item.store] = [item]
then add it to your context and have your template look like this:
{% for store, items in sorted_groceries.items %}
<div>{{store}}
<ul>
{% for item in items %}
<li>{{item}}</li>
{% endfor %}
</ul>
</div>
{% endfor %}

How about using ifchanged https://docs.djangoproject.com/en/1.7/ref/templates/builtins/#ifchanged
Assuming shopping_list is sorted by store:
{% for item in shopping_list %}
{% ifchanged %}<h3>{{ item.store.name }}</h3>{% endifchanged %}
{{ item.name }}
{% endfor %}

Related

Django: Nested Loop In Template

category = ['cat1','cat2','cat3']
inventory = CurrentInventory.objects.all()
for cats in categories
inventorybycat = inventory.filter(category =cats)
setofinventories.append(inventorybycat)
dictofstuff = [zip(setofinventories,categories)]
context = {
'setofinventories':setofinventories
'category':category
'dictofstuff':dictofstuff
}
In views.py above this loop creates a list of objects per each category.
In the template below this loop prints a filtered object list per every item in the category list.
{% for inventory in setofinventories%}
{% for item in inventory %}
{{ item.category }}
{{ item.productName }}
{% endfor %}
{% endfor %}
The only thing I am missing is I do not now how to reference the category in the template. I pass the whole list in context, but {{category{{forloop.counter}}}} is not a valid statement.
I would either like to use zip(category,setofinventories) to pass these two items together,
or create a category model, filter by that model and then I can reference that model by item?
If I zip these items dictofstuff = [zip(setofinventories,categories)]
How do I reference the category in the template?
{% for inventory,categories in dictofstuff %}
{% for inventory in setofinventories%}
{% for item in inventory %}
{{ item.quantity }}
{{ item.productName }}
{% endfor %}
{% endfor %}
{% endfor %}
Returns Error: "Need 2 values to unpack in for loop: got 1."
If i right understand you, you want to print category only one time.
In your case:
{% for inventory in setofinventories%}
{% ifchanged inventory.category %}
{{ inventory.category }}
{% endifchanged %}
{% for item in inventory %}
{{ item.quantity }}
{{ item.productName }}
{% endfor %}
{% endfor %}
i use ifchanged, more here:
https://docs.djangoproject.com/en/4.1/ref/templates/builtins/#ifchanged
in views i zipped this to a *listofstuff. Forgive the nomenclature.
dictofstuff = [zip(categories,setofinventories)]
in template. Again, forgive bad nomenclature. I should go back and rename these but it works.
{% for category in dictofstuff %}
{% for item in category %}
{{item.0}}
{% for stuff in item %}
{% for thing in stuff %}
{{thing.productId}}{{thing.productName}}
{% endfor %}
{% endfor %}
{% endfor %}
{% endfor %}

Get n items in django model using releverse relationship

Is possible to get the last n items in the Django model, using the reverse relationship.
{% for brand in brands %}
{% for product in brand.product_set.all %}
{% endfor %}
{% endfor %}
I am tried in this way, But it prints all things. But want only last 3 items
You can make use of the |slice template tag [Django-doc] to slice the collection with:
{% for brand in brands %}
{% for product in brand.product_set.reverse|slice:":3" %}
…
{% endfor %}
{% endfor %}

API returning an array inside of an object how to display contents- Django

So I am requesting spot price from cex API, it returns something like this
{"data":[{"amount: "67000.0", "base": "BTC", "currency": "USD"}]} of course there is more than one returned so I want to loop over it.
In my views.py I am passing it into my context 'price': price. Then inside of my .html file I have a list with a for loop for example:
<ul>
{% for x in price %}
<li>{{ x }}</li>
{% endfor %}
</ul>
Then when I open my .html page I get data But what I'd like to be able to do is make a table where I have three columns to show the amount, base and currency. I am unsure on how to extract this data individualy?
You can enumerate over the .values of price, and then enumerate over the dictionaries in that list with:
<table>
{% for vs in price.values %}
{% for v in vs %}
<tr><td>{{ v.amount }}</td><td>{{ v.base }}</td><td>{{ v.currency }}</td></tr>
{% endfor %}
{% endfor %}
</table>
In Django templates, you can access to dictionary items with a expresión like {{ dictionary.key }}
<ul>
{% for x in price %}
<li>{{ x.amount }}</li>
{% endfor %}
</ul>
See the docs on https://docs.djangoproject.com/en/3.2/ref/templates/api/#variables-and-lookups

Looping throgh json jinja2 python

I have a json fron an API like this:
my views.py looks like this:
def index(request):
movieData = requests.get('https://api.themoviedb.org/3/search/movie?query=Ishtar&api_key=....').json()
return render(request, 'dashboard/index.html', {'movieData': movieData})
My html looks like this:
{% for item in movieData %}
<lu>
<li>
{{ item.results.id }}
</li>
</lu>
{% endfor %}
However the loop is not working, it's not getting the data, this is how it looks:
Can someone give me hand please? I'm learning.
I'm trying to get the id of the 4 movies on the json result
Many thanks
You don't seem to be iterating over the right thing. The array is inside results.
{% for item in movieData.results %}
<li>{% item.id %}</li>
{% endfor %}
This is the correct answer:
{% for item in movieData.results %}
<li>{{ item.id }}</li>
{% endfor %}

Insert separator only if didn’t insert one before

I have a template variable product_list, which is a QuerySet of Product objects; Product objects, in turn, have a one-to-many field of Track objects (reverse mapped from Track, of course), which is possible to be empty. I want to create a list of Tracks, grouped by Products like this:
{% for product in product_list %}
{% if this is not the first product with tracks %}
<li class="separator"></li>
{% endif %}
{% for track in product.tracks %}
<li>{{track}}</li>
{% endfor %}
{% endfor %}
The question is, what should I write of if this is not the first product with tracks? I tried ifchanged product but it inserts a separator even on the first iteration (as it changes from "" to "someproduct"). forloop.counter is also not usable here, as it is possible that the first two products won’t have tracks.
One workaround could be to change product_list to track_list like this:
track_list = Track.objects.order_by('product__name')
so I can indeed use ifchanged. It is doable in my case, but I’m still interested in a solution for my first method.
You should compose the condition. It looks simple, perhaps is not this that your are asking for.
{% for product in product_list %}
{% if not forloop.first and product.tracks %}
<li class="separator"></li>
{% endif %}
{% for track in product.tracks %}
<li>{{track}}</li>
{% endfor %}
{% endfor %}
If this is not a solution for you, I suggest to you to cook data on view and send ready to be parse to template, more easy.
{% for product in product_list %}
{% if product.should_have_separator %}
<li class="separator"></li>
{% endif %}
{% for track in product.tracks %}
<li>{{track}}</li>
{% endfor %}
{% endfor %}
In your view append should_have_separator field dynamically to products should have it:
product_list = Product.objects.....
is_the_first_product = True
for product in product_list:
is_the_first_product_with_tracks = ( is_the_first_product
and bool( product.tracks ) )
product.should_have_separator = not is_the_first_product_with_tracks
is_the_first_product = False

Categories