Django _Categorize products - django

I want to show specific categories of items on a single page. Each category has a subheading and specific categories in the group. Whenever i run the enve, everything is good but each subcategories output all the products, even the ones in other categories. How can i rectify this.
def waterProducts(request):
category = request.POST.get('category')
if category == None:
products = Product.objects.order_by('-price').filter(is_published=True)
else:
products = Product.objects.filter(categoryname=category)
categories = Category.objects.all()
context = {
'products': products,
'categories': categories
}
return render(request, "products/WaterProductPage.html", context)
Above is my view.py files where i confirm if there is any category.
<main style="background-image: url(images/waterrocks.jfif)">
<section class="body-section">
<div class="all-products">
<!--Water coolers-->
{%for category in categories%}
<section class="main-products cooler">
<div class="upper-bar">
<div>
<h2>{{category.name}}</h2>
</div>
<div class="sortby">
<span>Sort By: <select class="sort">
<option value="highest">Highest Price</option>
<option value="lowest">Lowest Price</option>
</select></span>
</div>
</div>
<hr />
<!--Single Products-wrap-->
<div class="specific-product-wrap specific-product-wrap-cooler">
{%if products%}
{%for product in products%}
<a href="{%url 'product' product.pk%}">
<div class="specific-single-product">
<div class="product-image center">
<img class="product-image-shape" src="{{product.image.url}}" alt="adamol Distilled" />
</div>
<div class="produc-descriptions">
<h4 class="specific-product-title">{{product.title}}</h4>
<p class="one-line-description"></p>
<p class="price">Ksh.{{product.price}}</p>
<button type="button" class="AddToCart">Add To Cart</button>
</div>
</div>`
</a>
{%endfor%}
{%else%}
<p>No Product Lol</p>
{%endif%}
</div>
</section>
{%endfor%}
The above is my html template where i output each category with related product.
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Product(models.Model):
image = models.ImageField(null=False, blank=False)
title = models.CharField(max_length=2000, null=False, blank=False)
category = models.ForeignKey(
Category, on_delete=models.CASCADE, default=True, null=False)
price = models.DecimalField(max_digits=5, decimal_places=2)
description = models.TextField()
is_published = models.BooleanField(default=True)
created_at = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.title
The above code is for model.py where i declaire category as a foregn key.
from django.urls import path
from .import views
urlpatterns = [
path('waters', views.waterSamples, name='waters'),
path('services', views.waterServices, name="services"),
path('products', views.waterProducts, name='products'),
]
The above code is urls.py
I want the products to be shown like this...
on the same page
enter image description here

You fetch the products related to a Category with category.product_set, so:
{% for category in categories %}
<!-- … -->
<h2>{{category.name}}</h2>
<!-- … -->
{% for product in category.product_set.all %}
<a href="{%url 'product' product.pk %}">
<!-- … -->
<h4 class="specific-product-title">{{ product.title }}</h4>
<!-- … -->
{% empty %}
<p>No Product Lol</p>
{% endfor %}
{% endfor %}
The category.product_set.all will retrieve all related Products for a given Category, and thus not render all the ones.
If you only want to use Products with is_published=True and ordered by price, you can use a Prefetch object:
from django.db.models import Prefetch
def waterProducts(request):
categories = Category.objects.prefetch_related(Prefetch(
'product_set',
queryset=Product.objects.filter(is_published=True).order_by('-price')
))
context = {
'categories': categories
}
return render(request, "products/WaterProductPage.html", context)

In your views file in the else part, shouldn't it be
products = Product.objects.filter(category=category)

Related

How can show categories products in home page?

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 class="text-start montserrat_alternates_font ">{{products.product_category}}</h4>
<hr>
<!--- product card --->
<div class="col mb-5">
<div class="card h-100">
<!-- Sale badge-->
{% if products.offer_price != None %}
<div class="badge bg-dark text-white position-absolute" style="top: 0.5rem; right: 0.5rem">
SALE
</div>
{% endif %}
<!-- Product image-->
<img class="card-img-top" style="height:150px;" src="{{products.product_image.url}}" alt="..." />
<!-- Product details-->
<div class="card-body ">
<div class="text-center">
<!-- Product name-->
<h5 class="fw-bolder product_title" style="">{{products.product_title}}</h5>
</div>
</div>
</div>
</div>
{% endfor %}
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 :)

How to query foreign key in Django with if conditions and do something if a condition is met

I'm working a website where I want to display product that are of two categories which are premium and free package and I want to filter all the premium package and label it with a star or a premium text to indicate it a premium package and for the free I'll do nothing. I don't know if I should user foreign key for this or tuple.
models.py:
STATUS_CHOICE = (
('draft', 'Draft'),
('in_review', 'In Review'),
('published', 'Published')
)
class Package_Category(models.Model):
title = models.CharField(max_length=10000, verbose_name="Title")
slug = models.SlugField(max_length=1000, unique=True)
def get_absolute_url(self):
return reverse("package-categories", args=[self.slug])
def __str__(self):
return self.title
class Meta:
verbose_name = "Package Category"
verbose_name_plural = "Package Categories"
class Vectors(models.Model):
title = models.CharField(max_length=10000, null=True, blank=True, verbose_name="Title")
slug = models.SlugField(unique=True)
image = models.ImageField(upload_to="vectors-images/%Y/%m/%d/", default="default.jpg", verbose_name="Image Cover")
vec_file = models.FileField(upload_to='vector-uploads/%Y/%m/%d/', null=True, blank=True, verbose_name="Upload File")
category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name="Category")
package_category = models.ForeignKey(Package_Category, on_delete=models.CASCADE, verbose_name="Package Category")
tags = models.ForeignKey(Tag, on_delete=models.CASCADE, verbose_name="Tag")
status = models.CharField(choices=STATUS_CHOICE, default="published", max_length=150, verbose_name='Status')
creator = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Creator")
creator_image = models.ImageField(upload_to="creators-images/%Y/%m/%d/", default="default.jpg", verbose_name="Creator Image")
created = models.DateTimeField(verbose_name="Created")
class Meta:
verbose_name = "Vector"
verbose_name_plural = "Vectors"
def get_absolute_url(self):
return reverse("vector-details", args=[self.slug])
def __str__(self):
return self.title
index.html:
{% for vector in vectors %}
<!-- Single Pricing Box -->
<div class="col-lg-4 col-md-6 col-sm-12">
<div class="_45lio">
<div class="_jk58o item_image_urip">
<img src="{{vector.image.url}}" class="img-fluid" alt="">
<i class="fa fa-download"></i>
<div class="image_urip_caption">
<div class="urip_caption_flex">
<div class="urip_author">
<div class="urip_avater">
<a href="author-detail.html.html" class="author-img">
<img src="assets/img/author.png" class="img-fluid" alt="" />
</a>
</div>
<div class="urip_avater_place">
<h3 class="urip_title">Adam vilson</h3>
<span>Liverpool, London</span>
</div>
</div>
</div>
<div class="urip_caption_last">
<div class="item_list_links">
<i class="fa fa-plus-circle"></i>
<i class="fa fa-heart"></i>
</div>
</div>
</div>
<div class="_io980 item_image_urip">
{% if package_category.premium %}
<h1>Premium Package</h1>
{% else %}
<h1>Free Package</h1>
{% endif %}
<h4 class="_95lp">{{vector.package_category}}</h4>
<span class="_ip76n">{{vector.category}}</span>
</div>
</div>
</div>
</div>
{% endfor %}
views.py:
from django.shortcuts import render
from . models import Vectors, Tag, Category, Package_Category
def index(request):
vectors = Vectors.objects.filter(status='published').order_by('-created')
context = {
'vectors': vectors,
}
return render(request, 'index.html', context)
you should do something like this if you are not using drf just by using the same model.
def index(request):
vectors = Vectors.objects.filter(status='published', package_category__title =="premimum").order_by('-created')
if vectors.exists():
// do something:
context = {
'vectors': vectors,
}
return render(request, 'index.html', context)
Although the best option would be to use serializers with drf

How to slice items generated from a forloop with conditionals in django

I have a list of categories as well as a list of products my template is in such a manner that it has category sections each with a display of products that belong to said categories. I created a for loop for categories so as to easily display category sections for each category I create. I then went on to create a forloop for products within the category forloop with a condition so as to match products with their actual category before they are displayed under their category section. how can I slice the resulting products to limit the number of products shown
Models.py
class Category(models.Model):
name = models.CharField(max_length=120)
image_263x629 = models.ImageField(upload_to='cat_imgs')
image_263x629_2 = models.ImageField(upload_to='cat_imgs')
image_263x629_3 = models.ImageField(upload_to='cat_imgs')
img_array = [image_263x629, image_263x629_2, image_263x629_3]
description = models.CharField(max_length=250)
def __str__(self):
return self.name
class SubCategory(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=300)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.name
#def get_absolute_url(self):
# return reverse('subcat_detail', args=[str(self.id)])
class Product(models.Model):
name = models.CharField(max_length=120)
price = models.FloatField()
image_182x182 = models.ImageField(upload_to='pdt_imgs/')
image_1200x1200 = models.ImageField(upload_to='pdt_imgs/alt_imgs/')
image_600x600 = models.ImageField(upload_to='pdt_imgs/alt_imgs/')
image_600x600_2 = models.ImageField(upload_to='pdt_imgs/alt_imgs/')
image_300x300 = models.ImageField(upload_to='pdt_imgs/alt_imgs/')
img_array = [image_1200x1200, image_600x600, image_600x600_2]
sku = models.IntegerField()
available = models.BooleanField(default=True)
discount = models.IntegerField(default = 0)
description = models.CharField(max_length=120, blank=True, null=True)
brand = models.CharField(max_length=120, blank=True, null=True)
category = models.ForeignKey(SubCategory, on_delete=models.CASCADE)
seller = models.ForeignKey(Seller, on_delete=models.CASCADE)
Views
class HomePageView(ListView):
model = SubCategory
template_name = 'home.html'
queryset = SubCategory.objects.all()
def get_context_data(self, **kwargs):
context = super(HomePageView, self).get_context_data(**kwargs)
context['products'] = Product.objects.all()
context['pdts'] = Product.objects.order_by('?')[:12]
context['categories'] = Category.objects.all()
context['subcategories'] = SubCategory.objects.all()
return context
Template
{% for category in categories %}
<div class="ps-block--products-of-category">
<div class="ps-block__categories">
<h3>{{ category.name }}</h3>
<ul>
{% for subcategory in subcategories %}
{% if subcategory.category.name == category.name %}
<li>{{ subcategory.name }}</li>
{% endif %}
{% endfor %}
</ul><a class="ps-block__more-link" href="{% url 'cat_detail' category.id %}">View All</a>
</div>
<div class="ps-block__slider">
<div class="ps-carousel--product-box owl-slider" data-owl-auto="true" data-owl-loop="true"
data-owl-speed="7000" data-owl-gap="0" data-owl-nav="true" data-owl-dots="true" data-owl-item="1"
data-owl-item-xs="1" data-owl-item-sm="1" data-owl-item-md="1" data-owl-item-lg="1" data-owl-duration="500"
data-owl-mousedrag="off">
<img src="{{ category.image_263x629.url }}" alt="">
<img src="{{ category.image_263x629_2.url }}" alt="">
<img src="{{ category.image_263x629_3.url }}" alt="">
</div>
</div>
<div class="ps-block__product-box">
{% for product in products %}
{% if product.category.category.name == category.name %}
<div class="ps-product ps-product--simple">
<div class="ps-product__thumbnail"><a href="{% url 'pdt_detail' product.id %}"><img src="{{ product.image_300x300.url }}"
alt=""></a>
{% if product.discount > 0 %}
<div class="ps-product__badge">-{{ product.discount }}%</div>
{% endif %}
{% if product.available == False %}
<div class="ps-product__badge out-stock">Out Of Stock</div>
{% endif %}
</div>
<div class="ps-product__container">
<div class="ps-product__content" data-mh="clothing"><a class="ps-product__title"
href="{% url 'pdt_detail' product.id %}">{{ product.name }}</a>
<div class="ps-product__rating">
<select class="ps-rating" data-read-only="true">
<option value="1">1</option>
<option value="1">2</option>
<option value="1">3</option>
<option value="1">4</option>
<option value="2">5</option>
</select><span>01</span>
</div>
<p class="ps-product__price sale">UGX{{ product.price }}</p>
</div>
</div>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
Please do not filter in the template. You should filter in the view. A template implements rendering logic, not business logic*.
You can filter and slice in the view with:
def my_view(request):
# …
products = Product.objects.filter(category__name='specified category')[:10]
context = {
'products': products
}
return render(request, 'my_template.html', context)
This is not only the place where filtering belongs, it is also more efficient since we here will filter and slice on the database side. Typically a database can do this more efficient, and it furthermore limits the bandwidth from the database to the Django/Python layer.
Note (based on #SLDem's comment):
If you aim to filter children, you make use of a Prefetch object [Django-doc]. Indeed, imagine that we have a QuerySet of Categorys and we want to only retain Products that are available, we can use:
from django.db.models import Prefetch
categories = Category.objects.prefetch_related(
Prefetch(
'product_set',
Product.objects.filter(available=True),
to_attr='available_products'
)
)
then in the template we can render this with:
{% for category in categories %}
{% for product in category.available_products %}
…
{% endfor %}
{% endfor %}

How to list my categories and forums related to it? Django

Model
class Category(models.Model):
class Meta():
verbose_name_plural = "Categories"
cat_name = models.CharField(max_length=50)
description = models.TextField()
def get_forums(self):
get_forum = Forum.objects.filter(category=self)
return get_forum
def __str__(self):
return f"{self.cat_name}"
class Forum(models.Model):
class Meta():
verbose_name_plural = "Forums"
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="forums")
parent = models.ForeignKey('self', blank=True, null=True, on_delete=models.CASCADE)
forum_name = models.CharField(max_length=50)
description = models.TextField()
def __str__(self):
return f"{self.forum_name}"
Views
class Home(ListView):
model = Category
template_name = 'forums/index.html'
context_object_name = 'category'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['cat'] = Category.objects.all()
return context
HTML
{% block content %}
{% for cat in category %}
<div class="row">
<div class="bg-success rounded-top border border-dark" style="width:100%; padding-left:8px;">
{{cat.cat_name}}
</div>
</div>
<div class="row">
<div class="bg-secondary border border-dark" style="width:100%; padding-left:16px;">
Forums_Go_Here
</div>
</div>
{% endfor %}
{% endblock content %}
I am trying to get a homepage where I would be able to list my categories and show the forums in those categories.
The template I have is running a for loop which is looping through all Categories.
In the shell i am able to get the forums with the: Category.objects.get(pk=2).get_forums() command. But this limits it to one category.
You can use related name for that, no need to use additional method:
{% block content %}
{% for cat in category %}
<div class="row">
<div class="bg-success rounded-top border border-dark" style="width:100%; padding-left:8px;">
{{cat.cat_name}}
</div>
</div>
{% for forum in cat.forums.all %}
<div class="row">
<div class="bg-secondary border border-dark" style="width:100%; padding-left:16px;">
{{forum.forum_name}}
</div>
</div>
{% endfor%}
{% endfor %}
{% endblock content %}
Also you have a mistake there:
context['category'] = Category.objects.all()
If you want to access it as category in template put it there with that key, not cat.

How can i show sub-category in category into data collapse, Using Djagno

I want to show subcategory in category into data collapse using django. As I am a noob developer that's why i can't understand the logic. Category and Sub-Category model is,
category.py
class Category(models.Model):
category = models.CharField(max_length=120)
timestamp = models.DateTimeField(auto_now_add=True)
subcategory.py
class SubCategory(models.Model):
sub_category = models.CharField(max_length=120)
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
I wanna show those value using generic.ListView
views.py
class PagetListView(ListView):
model = ModelName
template_name = 'template.html'
context_object_name = 'main_content'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['category_list'] = Category.objects.all()
# context['sub_category'] =
return context
I wanna show just like this,
Suppose category is an instance of Category model
category.subcategory_set.all()
will give you query-set of all subcategories fo a perticular category
So u just need to iterate over all the categories and get subcategories for each
I solve the problem. Its nothing just a logic with if else.
<div>
{% for category in category_list %}
<div id="headingTwo">
<h5 class="mb-0">
<button class="btn btn-link collapsed" data-toggle="collapse" data-target="#{{ category.category }}" aria-expanded="false" aria-controls="collapseTwo">
{{ category.category }}
</button>
</h5>
</div>
<div id="{{ category.category }}" class="collapse" aria-labelledby="headingTwo" data-parent="#accordion">
<div class="sub-category">
{% for sub_cat in sub_category %}
{% if sub_cat.category == category %}
{{ sub_cat.sub_category }}<br/>
{% endif %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>