Django Template - filtering content - python

I have two models - "Products" and "Categories", and every product may be added to existing category.
I am trying to find a way to render page with products, filtered by category.
Currently I did it for every category by manualy filtering it in template:
{% for instance in object_list %}
{% if instance.category.categoryname == "Statues" %}
{{ instance.name }}
{{ instance.description }}
{{ instance.price }}
{% endif %}
{% endfor %}
I have same template for every category ("Paintings", "Jewelry" etc) and changed condition in each template. URL "../Statues" leads to prexisting template
Is there any way to do it easier?
I would like condtition {% if instance.category.categoryname == "Statues" %} to be imported from URL. So when you access "../Jewelry" - template would import "Jewelry" from URL and filter content accordingly.
models.py
class Category(models.Model):
categoryname = models.CharField(max_length=20)
description = models.CharField(max_length=200, blank=True, null=True)
#To make in name, not objXXX
def __str__(self):
return self.categoryname
class Product(models.Model):
name = models.CharField(max_length=20)
image = models.ImageField(upload_to='static/photos', default='http://placehold.it/700x400')
description = models.TextField(max_length=200, blank=True, null=True)
price = models.DecimalField(decimal_places=2, max_digits=10)
category = models.ForeignKey(Category, on_delete=models.PROTECT, blank=True, null=True)
#To make in name, not objXXX
def __str__(self):
return self.name
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('<str:categoryname>', category_filter)
]
view.py
def category_filter(request, categoryname):
queryset = Product.objects.all()
context = {"object_list": queryset}
return render(request, "category_filter.html", context)
category choice template:
{% for instance in category_list %}
{{ instance.categoryname }}
{% endfor %}

It may be too simple answer ...you can apply filter in your views and send query set based on that:
def category_filter(request, categoryname):
category=Category.objects.get(categoryname=categoryname)
queryset = Product.objects.filter(category=category)
context = {"product_list": queryset}
return render(request, "category_filter.html", context)

Related

How can I get all objects of a model and it's fields?

I have a Product Model and I want to be able to count all it's objects in a method so I can render the total number in a template, same for each category but I can only do that with the number_of_likes method.
class Category(models.Model):
name = models.CharField(max_length=45)
...
class Product(models.Model):
author = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
title = models.CharField(max_length=120, unique=True)
category = models.ForeignKey(Category, default=None, on_delete=models.PROTECT)
product_type = models.CharField(max_length=30, choices=TYPE, default='Physical')
likes = models.ManyToManyField(User, related_name='like')
...
def number_of_likes(self):
return self.likes.count()
def number_of_products(self):
return self.Products.objects.all.count()
def number_of_products_for_category(self):
return Product.objects.filter(category_id=self.category_id).count()
def __str__(self):
return str(self.title) + ' from ' + str(self.author)
<div>
<h3>All products ({{ number_of_products }})</h3>
{% for category in categories %}
<p>{{ category.name }} (q)</p>
{{ number_of_products_for_category }}
{% endfor %}
</div>
The number_of_products and number_of_products_for_category are the methods that aren't working.
You can work with:
def number_of_products_for_category(self):
return Product.objects.filter(category_id=self.category_id).count()
But if you use this for all categories, you can .annotate(..) [Django-doc] the queryset:
from django.db.models import Count
categories = Category.objects.annotate(num_products=Count('product'))
and render with:
{% for category in categories %}
<p>{{ category.name }} ({{ category.num_products }})</p>
{% endfor %}
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Django template - render object with specific attribute

I want my template to render object with specific attribute (products with specific category).
I am trying following code but it doesn't work.
May you please suggest how to write it correctly?
Template that I am trying make to work:
{% for instance in object_list %}
{% if instance.category == 'Statues' %}
{% endif %}
{% endfor %}
my models.py
class Category(models.Model):
categoryname = models.CharField(max_length=20)
description = models.CharField(max_length=200, blank=True, null=True)
#To make in name, not objXXX
def __str__(self):
return self.categoryname
class Product(models.Model):
name = models.CharField(max_length=20)
image = models.ImageField(upload_to='static/photos', default='http://placehold.it/700x400')
description = models.TextField(max_length=200, blank=True, null=True)
price = models.DecimalField(decimal_places=2, max_digits=10)
category = models.ForeignKey(Category, on_delete=models.PROTECT, blank=True, null=True)
#To make in name, not objXXX
def __str__(self):
return self.name
You need to use the field on the object;
{% for instance in object_list %}
{% if instance.category.categoryname == 'Statues' %}
{{ instance }}
{% endif %}
{% endfor %}
It won't work as a comparison against the string representation of the object.

Django - Grouping 2 context datasets into 1 forloop

For each Product I need to show all it's reviews with an attached profilepicture, this requires queries into 3 models but I need a maximum of 2 for loops as when I do the Profile for loop all the images appear together as shown below. Ideally review & profile datasets are put together so only one profilepicture appears for each review.
Is there a way to fix this issue?
models.py
class Product(models.Model):
name = models.CharField(max_length=100)
brand = models.CharField(max_length=100)
cost = models.DecimalField(max_digits=8, decimal_places=2, default=0.00)
category = models.CharField(max_length=100)
releasedate = models.DateField()
description = models.TextField()
productphoto = models.ImageField(default='products/default_product.jpg', upload_to='products')
class Review(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
rating = models.PositiveSmallIntegerField(default=1, validators = [MinValueValidator(1), MaxValueValidator(5)])
reviewtext = models.TextField()
class Profile(models.Model):
user = models.OneToOneField(User, on_delete = models.CASCADE)
profilephoto = models.ImageField(default='profiles/default_profile.jpg', upload_to='profiles')
views.py
class ProductDetailView(TemplateView):
# template_name = 'reviewApp/test.html'
template_name = 'reviewApp/product_detail.html'
def get_context_data(self, **kwargs):
prod = self.kwargs['pk']
context = super(ProductDetailView, self).get_context_data(**kwargs)
context['Products'] = Product.objects.filter(id=prod)
context['Reviews'] = Review.objects.filter(product=prod)
profile_ids = Review.objects.filter(product=prod).values_list('profile_id', flat=True)
context['Profiles'] = Profile.objects.filter(id__in=profile_ids)
return context
product.html
{% for prod in Products %}
<img src="{{prod.productphoto.url}}">
{{ prod.brand }} {{ prod.name }}
£{{ prod.cost }}
{{ prod.category }}
{{ prod.releasedate }}
{{ prod.description }}
{% endfor %}
{% for rev in Reviews %}
{% for prof in Profiles %}
<img src="{{prof.profilephoto.url }}">
{% endfor %}
{{ rev.rating }}
{{ rev.author }}
{{ rev.reviewtext }}
{% endfor %}
view
You can query the profile picture as rev.profile.profilephoto

How to filter categories in Django

I have a little problem. I want to create something like a webpages directory. In my model I've create a class Kategorie, and class Firma. Class Kategoria creating main categories and subcategories. In class Firma I can define in witch category and subcategory the new record will be belong. My question is: How to display in html on main page main categories and little lower the subcategories like in this picture
Here is my code
models.py
from django.db import models
from django.contrib.auth.models import User
class Kategoria(models.Model):
name = models.CharField(max_length=250, verbose_name='Kategoria')
slug = models.SlugField(unique=True,verbose_name='Adres SEO')
parent = models.ForeignKey('self', blank=True, null=True, related_name='children', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name = 'Kategoria'
verbose_name_plural = 'Kategorie'
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' / '.join(full_path[::-1])
class Firma(models.Model):
user = models.ForeignKey(User, default=1, verbose_name='Użytkownik', on_delete=models.CASCADE)
title = models.CharField(max_length=250, verbose_name='Nazwa firmy')
slug = models.SlugField(unique=True, verbose_name='Adres SEO')
category = models.ForeignKey('Kategoria', null=True, blank=True, verbose_name='Kategoria', on_delete=models.CASCADE)
content = models.TextField(verbose_name='Opis')
draft = models.BooleanField(default=False, verbose_name='Szablon')
publish = models.DateField(auto_now=False, auto_now_add=False)
class Meta:
verbose_name='Firma'
verbose_name_plural='Firmy'
def __str__(self):
return self.title
views.py
from django.shortcuts import render, get_object_or_404
from .models import Kategoria, Firma
def widok_kategorii(request):
kategorie = Kategoria.objects.filter().order_by('name')
context = {'kategorie': kategorie}
return render(request, 'ogloszenia/index.html', context=context)
index.html
{% include 'ogloszenia/header.html' %}
Wyświetl kategorie<br>
{% for kategoria in kategorie %}
<b>{{kategoria.name}}<br></b>
{% endfor %}
{% include 'ogloszenia/footer.html' %}
So the problem is sub categories, right?
You can use inclusion_tag in your template as i mentioned here once:
Tree Structure (Foreign Keys to itself) and templates
If you need to render multiple level of sub categories then just do as i mentioned in the link.
But if you just need the first level, then it's pretty simple:
views.py:
Getting categories without any parent (line #2)
def widok_kategorii(request):
### Get the categories without any parent.
kategorie = Kategoria.objects.filter(parent=None).order_by('name')
context = {'kategorie': kategorie}
return render(request, 'ogloszenia/index.html', context=context)
Template:
{% include 'ogloszenia/header.html' %}
Wyświetl kategorie<br>
<ul>
{% for kategoria in kategorie %}
<li>
{{kategoria.name}}
{% if kategoria.children.count > 0 %}
<ul>
{% for sub in kategoria.children.all %}
<li>{{ sub.name }}</li>
{% endfor %}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% include 'ogloszenia/footer.html' %}
You can just design it to be look like the picture you posted but this is the way to achive the tree structure for such a design.

Django QuerySet object has no attribute 'objects

I have problem with show product in category. (Products are show but when i click on category (tshirt) i have this problem AttributeError at /man/tshirt/
'QuerySet' object has no attribute 'objects
views.py
def product_list(request, category_slug=None):
category = None
categories = Category.objects.all()
products = Product.objects.filter(section='man', available=True)
if category_slug:
category = get_object_or_404(Category, slug=category_slug)
products = products.objects.filter(category=category)
return render(request,
'shop/product/list.html',
{'category': category,
'categories': categories,
'products': products})
urls.py
urlpatterns = [
url(r'^$', views.main, name='main'),
url(r'^man/$', views.product_list, name='product_list'),
url(r'^man/(?P<category_slug>[-\w]+)/$',
views.product_list,
name='product_list_by_category'),
]
models.py
class Category(models.Model):
name = models.CharField(max_length=200,
db_index=True)
slug = models.SlugField(max_length=200,
db_index=True)
class Meta:
ordering = ('name',)
verbose_name = 'category'
verbose_name_plural = 'categories'
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('shop:product_list_by_category',
args=[self.slug])
list.html
{% extends "shop/base.html" %}
{% load static %}
{% block title %}VPW{% endblock %}
{% block content %}
<ul>
{% for c in categories %}
<li>
{{c.name}}
</li>
{% endfor %}
</ul>
{% for product in products %}
{{ product.name }}
{% endfor %}
{% endblock %}
You should change
products = products.objects.filter(category=category)
to
products = products.filter(category=category)
In short, you've already queried data at the objects level, thus the .objects identifier is no longer necessary or valid at this point in the code.
You can find more info here.

Categories