Placing items below category in template - python

So I wrote a small shopping list application using Django. Users may enter their desired items which are stored in a database alongside a category they reside in to make the list presented to the user more clean and giving them (the users) a good overview of what they are going to buy.
The goal is to show the user a list which is categorized, something like this:
VEGETABLES
Paprika
Tomatoes
VARIOUS
Toothpaste
Toilet Paper
And so on. I have like five categories saved in my database and users may choose one corresponding category once they add an item to the list below which the item will be displayed in the list.
These are my database models:
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=20)
tag = models.CharField(max_length=2)
def __str__(self):
return self.name
class Item(models.Model):
text = models.CharField(max_length=40)
count = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
complete = models.BooleanField(default=False)
def __str__(self):
return self.text
this is my views.py
def items(request):
item_list = Item.objects.order_by('id')
categories = Category.objects.all()
form = ItemForm()
context = {'item_list' : item_list, 'form' : form, 'categories' : categories}
return render(request, 'buyit/index.html', context)
and this is my index.html (very basic, stripped off all the css and html stuff):
{% for category in categories %}
<h4>{{ category.name }}</h4>
{% for item in category.item_set.all() %}
{{ item.text }}
{% endfor %}
{% endfor %}
I got this functions from the Jinja2 template from a code snippet and it might be, that I didn't understand it correctly.
However, the debugger tells me:
TemplateSyntaxError at /
Could not parse the remainder: '()' from 'category.item_set.all()'
Any hints on what I am doing wrong?
Simply displaying the categories worked fine but after adding
{% for item in category.item_set.all() %}
{{ item.text }}
{% endfor %}
things crashed.
Any help is highly appreciated!
Thanks in advance!

You can't use () in django templates. Just remove them to call the function / method.
From https://docs.djangoproject.com/en/3.1/ref/templates/language/#variables
Behind the scenes
Technically, when the template system encounters a dot, it tries the following lookups, in this order:
Dictionary lookup
Attribute or method lookup
Numeric index lookup
If the resulting value is callable, it is called with no arguments. The result of the call becomes the template value.

try this:
views.py:
def items(request):
commoditys = {category: Item.objects.filter(category=category) for category in Category.objects.all()}
form = ItemForm()
context = {'commoditys' : commoditys, 'form' : form}
return render(request, 'buyit/index.html', context)
index.html:
{% for category, commoditys in commoditys.items %}
<h3>{{ category.name }}</h3>
{% for commodity in commoditys %}
{{ commodity.text }}
{% endfor %}
{% endfor %}
Change the Category variable to a dictionary, where the Key of each item is the Category object, and its corresponding value is the item Queryset it belongs to

Related

How to access model.field in template? django

I started learning django few days ago and trying to build my first blog.
My problem is that I decided to add an extra field for my categories (subheading), which I want to be in my template, but can't understand how to do it.
my models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=20)
subheading = models.CharField(max_length=160)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
link = models.TextField()
categories = models.ManyToManyField("Category", related_name="posts")
def __str__(self):
return self.title
views.py
from django.shortcuts import render
from blog.models import Post, Category
def blog_category(request, category):
posts = Post.objects.filter(
categories__name__contains=category
).order_by(
'title'
)
context = {
"category": category,
"posts": posts
}
return render(request, "blog_category.html", context)
The only way category.name or category.subheading are displayed in template (by the teacher) is inside {% for post in posts %} {% endfor %}:
{% for post in posts %}
{% for category in post.categories.all %}
{{ category.subheading }}
{% endfor %}
{% endfor %}
In this case, if there are 10 posts on category page, subheading repeats 10 times. I only need to print 1 to describe category.
Is there a way to call category.subheading outside of {% for post in posts %} ? Or somehow to print only one result.
p.s. sorry for my primitive English level.
You can do this with a Prefetch object [Django-doc]:
from django.db.models import Prefetch
def blog_category(request, category):
posts = Post.objects.filter(
categories__name__contains=category
).prefetch_related(
Prefetch(
'categories',
Category.objects.filter(name__contains=category)
to_attr='relevant_categories'
)
).order_by(
'title'
)
# …
In your template, you can then render this with:
{% for post in posts %}
{% for category in post.relevant_categories %}
{{ category.subheading }}
{% endfor %}
{% endfor %}
Not sure to understand what you want to do but you can search and access to Category elements by doing something like that:
categories=Category.objects.filter(name='NameYouWanttoSearch').values_list('subheading')
can add a model manager to the categories , take single instance and call it in templates instead of all.
class CategoryManager(models.Manager):
def single_category(self):
return self.get_queryset()[:1]
class Category(models.Model):
name = models.CharField(max_length=20)
subheading = models.CharField(max_length=160)
objects = CategoryManager()
def __str__(self):
return self.name
and in templates
{% for post in posts %}
{% for category in post.categories.single_category %}
{{ category.subheading }}
{% endfor %}
{% endfor %}

Listing items by the category they belong to

I'm a complete Newb so please bear with me. I've never used OOP or a framework before.
I'm creating a book keeping system where I want the ledgers to be displayed by the category they belong to. Here's my model and it seems to work:
class COAGroup(models.Model):
Name = models.CharField(max_length=200)
def __str__(self):
return self.Name
class Ledger(models.Model):
Name = models.CharField(max_length=200)
COAGroup = models.ForeignKey(COAGroup, on_delete=models.CASCADE)
def __str__(self):
return self.Name
I'm working on my first view which should list all the ledgers by the category they belong to. Eg
<h2>Some COAGroup Name</h2>
<ul>
<li>A ledger that belongs to this group</li>
<li>Another ledger that belonds to the group</li>
<li>And another</li>
</ul>
<h2>Another COAGroup</h2>
<ul>
<li>A ledger that belongs to this second group</li>
<li>Another ledger</li>
</ul>
I've written the following view in views.py:
def showledgers(request):
COAGroups = COAGroup.objects.all()
Ledgers = Ledger.objects.all()
context = {
"COAGroups":COAGroups,
"Ledgers":Ledgers,
}
return render(request,"ListLedgers.html",context)
I've managed to get the ledgers to show in ListLedgers.html but I can't figure out how to get them to list by COA Group as per my example.
Thank you!
In your view, you better already .prefetch_related(..) [Django-doc] the related Ledgers:
def showledgers(request):
COAGroups = COAGroup.objects.prefetch_related('ledger')
context = {
'groups': COAGroups,
}
return render(request, 'ListLedgers.html', context)
Then in the template, you can iterate over the COAGroups, and for each such group over the related .ledger_set:
{% for group in groups %}
<h2>{{ group.Name }}</h2>
<ul>
{% for ledger in group.ledger_set.all %}
<li>{{ ledger.name }}</li>
{% endfor %}
</ul>
{% endfor %}
Note: normally the name of the fields in a Django model are written in snake_case, not PerlCase, so it should be: name instead of Name.

querying foreignkey nested for loop django

I am trying to return a list of categories for a business, and for each category I would like to list all the items related to the category.
I was returning all of my items, not by category, but I have decided I want them sorted by category. This is what I have tried (among other attempts as well) I simply am having trouble getting the items into there categories. This is my latest attempt
In my models.py I have
Business(models.Model):
name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
logo = models.CharField(max_length=300)
ItemCategory(models.Model):
name = models.CharField(max_length=50)
Item(models.Model):
name = models.CharField(max_length=100)
business = models.ForeignKey(Business)
category = models.ForeignKey(ItemCategory)
short_description = models.CharField(max_length=250)
in my views.py
def business_profile(request, business_id):
items = Item.objects.filter(business_id = business_id).select_related('itemcategory')
return render(request, 'business_profile.html', {"items": items})
in my template I am trying
{% for itemcategory in items.itemcategory_set.all %}
{{ itemcategory.name }}
{% for item in itemcategory %}
{{ item.name }} <br>
{{ item.short_description }}
{% endfor %}
{% endfor %}
From my research into other questions and reading the documents, i felt this would be right.. However from my results I can not get the correct output in my template.
Would this be the correct practice? Or should I try getting the categories first with
categories = ItemCategory.objects.filter(business = business_id)
and then possibly add a select_related('item') to process all the items for the category?
When I refer to data in the template but my model has 2 words (ItemCategory) - for instance when I move backward for a foreign key or use the model in select_related('') - would I use item_category or do you simply use itemcategory with no underscore?
UPDATE: I answered below in comment with explanation.
To list the items by category, I used #yusuf.oguntola's idea and initially got the business with .get(), then I created a queryset to get all the items. My function in views.py included
business = Business.objects.get(id = business_id)
items = business.item_set.all().order_by('category')
note: the business_id was passed in url pattern
However, the key change was in my template. I found this awesome built in functionality for templates in the django documents here
I implemented it in template like so...
{% regroup items by category as category_list %}
<ul>
{% for category in category_list %}
<li>{{ category.grouper }}
<ul>
{% for item in category.list %}
<li>{{ item.name }}<br> {{ item.short_description }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
One of the most important parts is you have to have order_by() and put the parameter of the field you are ordering by - in my case, category. If this is not included, it will simply put the items listed in chronological order and will repeat your field instance.
items = Item.objects.filter(business_id = business_id) is wrong.
You should rather say:
items = Item.objects.filter(business__id = business_id) #Notice the double underscore.
You may also rather get the business and simply say:
business.item_set.all() #This would retrieve all Item for the business.
select_related() expects a fieldname as a paramter. So use a field name that exists on the Item model. Like:
select_related('category')
Where's the meal? May be you can proceed from there though.

How to get an ManyToManyField related queryset that can be iterated in Django?

lets say my models.py looks like this:
class Tag(models.Model):
name = models.CharField(max_length=50)
class Review(models.Model):
text = models.TextField()
tags = models.ManyToManyField(Tag, related_name='review_tags', blank=True)
In my views.py I am getting a queryset like this:
def index(request):
reviews = Review.objects.all()
return render(request, 'myapp/index.html', {'reviews' : reviews})
Now in index.html, I am doing something like this:
<div>
{% for review in reviews %}
<p>{{review.text}}</p>
{% for tag in review.tags %}
{{tag.name}}
{% endfor %}
{% endfor %}
</div>
Obviously, this does not work. However, it gives you an idea of what I am trying to do, that is, get the tags for a particular review and just display them. How do I do this with my current setup?
Thank you.
I am completely rewrited the answer, because it get upvotes and 'accepted answer', however initially it was wrong.
So the right answer would be to call {% for tag in review.tags.all %} to iterate through all Tag objects that relate to Review object in loop.
Adition: if you want firstly iterate though Tag objects and then iterate through Review objects that has a relation with Tag then you need to have view, that returns Tag objects.
def index(request):
tags = Tag.objects.all()
return render(request, 'index.html', {'tags': tags})
And in the template you need to iterate using related_name in ManyToManyField
{% for tag in tags %}
<p>{{tag.name}}</p>
{% for review in tag.review_tags.all %}
{{ review.text }}
{% endfor %}
{% endfor %}

Object is not iterable: Filtering QuerySet to display latest instance

I am trying to create a filter in a QuerySet using Django that returns the most recent instance that was submitted by the current logged in user.
I so far have the following in my view.py file:
def transfer(request):
title = 'Transfers'
queryset = Transfer.objects.filter(user=request.user).latest('timestamp')
context = {
"title": title,
"queryset": queryset,
}
if request.method == "POST":
print request.POST
return render(request, "transfer.html", context)
However this returns the error
TypeError: 'Transfer' object is not iterable
My models.py file looks like this so far:
from django.db import models
from django.contrib.auth.models import User
class Transfer(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User)
amount = models.DecimalField(max_digits=10, decimal_places=2)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
and my html file is calling the QuerySet in the following way:
{% if request.user.is_authenticated %}
<h2>Your Transfers</h2>
{% if queryset %}
{% for instance in queryset %}
<p>Amount: {{ instance.amount }} </p>
<p>User: {{ instance.user }} </p>
<p>Date: {{ instance.timestamp }}</p>
{% endfor %}
{% else %}
<h3>You have not made any transfers.</h3>
{% endif %}
{% endif %}
Any suggestions would be hugely appreciated!
latest returns a single object: the latest one. So you're no longer passing an iterable queryset to the template, but a single instance which naturally you can't iterate.
It's not clear what you want to do here; perhaps you just need to remove the for loop, and just refer to the instance directly. Alternatively, if you do want differing you can iterate over, you might want to remove the latest call and just order by reverse timestamp.
Update you query like this
ueryset = Transfer.objects.filter(user=request.user).order_by('timestamp')[count_limit:]
[count_limit:] means how many objects from top you want to get. For getting top 5 you can do [:5]
You do not have to add id field, as django automatically add this.

Categories