I want to show my all products row by row on the home page category-wise. Suppose, before starting a new row will have a heading(category name) and then will show all products according to the product category and then again will be starting a new row with a heading according to category. How can I do it? I applied the 3/4 methods but didn't work. Bellow, I've shown one of the methods. It doesn't work properly. Please help me.
views.py:
def home(request):
all_products = Products.objects.all()
context ={
"all_products":all_products,
}
return render(request,'home.html', context)
model.py:
class Category(models.Model):
title = models.CharField(blank=True, null=True, max_length = 100)
def __str__(self):
return str(self.title)
class Products(models.Model):
product_image = models.ImageField(blank=True, null=True, upload_to = "1_products_img")
product_title = models.CharField(blank=True, null=True, max_length = 250)
product_price = models.IntegerField(blank=True, null=True)
offer_price = models.IntegerField(blank=True, null=True)
created_date = models.DateTimeField(blank=True, null=True, auto_now=True)
product_category = models.ForeignKey(Category, related_name="categoty_related_name", on_delete=models.CASCADE, blank=True, null=True)
context_processors.py:
from .models import Category
def categories(request):
return {"categories":Category.objects.all()}
template:
{% for products in all_products %}
<h4 >{{products.product_category}}</h4>
<hr>
<!--- product card --->
<div >
<div >
<!-- Sale badge-->
{% if products.offer_price != None %}
<div style="top: 0.5rem; right: 0.5rem">
SALE
</div>
{% endif %}
<!-- Product image-->
<img style="height:150px;" src="{{products.product_image.url}}" alt="..." />
<!-- Product details-->
<div >
<div >
<!-- Product name-->
<h5 style="">{{products.product_title}}</h5>
</div>
</div>
</div>
</div>
{% endfor %}
CodePudding user response:
You have to change your thinking a bit :)
The easiest approach is to think from bigger "items" (like your Category
) to smaller (Product
).
Firstfully, use singular naming for models, like Product
(without 's', just like you did with Category
), because otherwise you will end up with confusing setting. Also, don't name fields with your model's name unless it's really necessary, product_category
should be simply category
and so on.
Secondly, open big for
loop with Category
that you pass as classic view's context:
def home(request):
categories = Category.objects.all()
context = {
"categories": categories,
}
return render(request,'home.html', context)
Then look at your ForeignKey field in Product
:
class Products(models.Model):
...
category = models.ForeignKey(Category, related_name="products", ...)
The key is to use related name
properly. In your template, try logic like this (using related_name
smoothly in reversed relationship):
{% for category in categories %}
{{ category.title }}
{% for product in category.products.all %}
{{ product.title }} - {{ product.price }}
{% endfor %}
{% endfor %}
If you don't set related_name
, then it would be accessible with <model_name>_set.all()
, in this case product_set.all()
(in templates without brackets, of course). Anyway, it will work like a charm :)