Django display related count - django

i currently try to display who many posts a category has.
Therefor i created the Post Model and the Category Model (See below):
models.py
# Categorys of Post Model
class Category(models.Model):
title = models.CharField(max_length=255, verbose_name="Title")
class Meta:
verbose_name = "Category"
verbose_name_plural = "Categories"
ordering = ['title']
def __str__(self):
return self.title
#Post Model
class Post(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField(max_length=10000)
category = models.ForeignKey(Category, verbose_name="Category", on_delete=models.CASCADE, null=True)
tag = models.CharField(max_length=50, blank=True)
postattachment = fields.FileField(upload_to='postattachment/%Y/%m/%d/', blank=True, null=True)
postcover = fields.ImageField(upload_to='postcover/%Y/%m/%d/', blank=True, null=True, dependencies=[
FileDependency(processor=ImageProcessor(
format='JPEG', scale={'max_width': 300, 'max_height': 300}))
])
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
category_list.html
{% extends 'quickblog/base.html' %}
{% block content %}
{% for categories in categories %}
<div>
<h1><u>{{ categories.title }} {{ $NumCountGetHere }}</u></h1>
</div>
{% endfor %}
{% endblock %}
Now i have no idea how to get the related objects counted...?

You can use something like this:
{% for cat in categories %}
<div>
<h1><u>{{ cat.title }} {{ cat.post_set.count }}</u></h1>
</div>
{% endfor %}
The model Post has a Foreignkey field to the model Category. You can access the related Post instances from a given Category instance using the manager category_instance.post_set. Read about it in the docs.
Finally, we use the method .count() on this manager to get the number of related posts for that given category. This way the code ends up looking like {{ cat.post_set.count }}.

Related

Django form selection box 'jammed' as active so cant make selections

USERS can Upvote or Downvote Posts/Projects posted by Other Users:
screenshot of 'jammed' active dropdown selection box
THIS CODE DIRECTLY BELOW is the models.py that contains the Project class (model) with the vote_total and vote_ratio fields.
It also contains the Review class (model) which is the basis for the ReviewForm in forms.py (code included later)
class Project(models.Model):
owner = models.ForeignKey(Profile, null=True, blank=True, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True)
featured_image = models.ImageField(null=True, blank=True, default="default.jpg")
demo_link = models.CharField(max_length=2000, null=True, blank=True)
source_link = models.CharField(max_length=2000, null=True, blank=True)
tags = models.ManyToManyField('Tag', blank=True)
vote_total = models.IntegerField(default=0, null=True, blank=True)
vote_ratio = models.IntegerField(default=0, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
def __str__(self):
return self.title
class Meta:
# ordering = ['-created']
ordering = ['-vote_ratio', 'vote_total', 'title']
AND here is the Review class (model)
class Review(models.Model):
VOTE_TYPE = (
('up', 'Up Vote'),
('down', 'Down Vote'),
)
owner = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
body = models.TextField(null=True, blank=True)
value = models.CharField(max_length=200, choices=VOTE_TYPE)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
class Meta:
unique_together = [['owner', 'project']]
def __str__(self):
return self.value
Here is the ReviewForm from the forms.py
class ReviewForm(ModelForm):
class Meta:
model = Review
fields = ['value', 'body']
labels = {
'value': 'Place your vote',
'body': 'Add a comment with your vote'
}
def __init__(self, *args, **kwargs):
super(ReviewForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs.update({'class': 'input'})
Here is the html template where the form is located
<div class="comments">
<h3 class="singleProject__subtitle">Comments</h3>
<h5 class="project--rating">
{{project.vote_ratio}}% Postitive ({{project.vote_total}} Vote{{project.vote_total|pluralize:"s"}})
</h5>
{% if request.user.profile.id in project.reviewers %}
<p>You have already submitted a comment!</p>
{% elif request.user.profile == project.owner %}
<p>You can't comment your own work!</p>
{% elif request.user.is_authenticated %}
<form class="form" action="{% url 'project' project.id %}" method="POST">
{% csrf_token %}
{% for field in form %}
<div class="form__field">
<label for="formInput#textarea">{{field.label}} </label>
{{field}}
</div>
{% endfor %}
<input class="btn btn--sub btn--lg" type="submit" value="Add Review" />
</form>
{% else %}
Please login to leave a comment
{% endif %}
It was working okay when first implemented and has somehow developed this issue
This is running in venv with Python 3.9.6
Thank you for considering this question !
ADDED - rendered html
rendered html

django filter can't find a right solution

my models:
class Order(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="order_user")
first_name = models.CharField(_('first name'), max_length=50)
last_name = models.CharField(_('last name'), max_length=50)
email = models.EmailField(_('e-mail'))
address = models.CharField(_('address'), max_length=250)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class OrderItem(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
product = models.ForeignKey(Product, related_name='order_items', on_delete=models.CASCADE)
shop = models.ForeignKey(MyShop, related_name='shop_order', on_delete=models.CASCADE)
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.PositiveIntegerField(default=1)
my views:
def shop_orders(request):
order = Order.objects.filter(items__shop=request.user.user_profile_shop)
return render(request, 'account/paneli/shop/orders/shop_orders.html', {'order': order,})
in the template,it show the order of the shop but it shows the OrderItem of all the shops
{% for order in order %}
order.id #it ok
{% for item in order.items.all %}
{{ item.product }}# wrong shows the OrderItem of all the shops
{% endfor %}
{% endfor %}
can you help me someone where i went wrong
the order has the products of many stores, I want to filter the order only with the products of the current shop
You can use prefetch_related with Prefetch() objects to filter on the related objects (plus this makes it more efficient by solving the N + 1 problem):
from django.db.models import Prefetch
def shop_orders(request):
orders = Order.objects.prefetch_related(
Prefetch(
'items',
queryset=OrderItem.objects.filter(shop=request.user.user_profile_shop).select_related('shop'),
to_attr='items_from_specific_shop'
)
)
return render(request, 'account/paneli/shop/orders/shop_orders.html', {'orders': orders,})
Then in the template you can write:
{% for order in orders %}
order.id
{% for item in order.items_from_specific_shop %}
{{ item.product }}
{% endfor %}
{% endfor %}
user reverse query using related name in shop field in order_item
def shop_orders(request):
order = Order.objects.filter(items__shop=request.user.user_profile_shop)
shop = current_shop#get a shop whose items you need
order_for_shop = shop.shop_order.all()
return render(request, 'account/paneli/shop/orders/shop_orders.html', {'order': order_for_shop,})

How to display Data of foreign key in Django html page?

I want to display a Company header and the products below its related company. I am new to django i do not understand this fully.
My models.py
class Company(models.Model):
name = models.CharField(max_length=250)
def __str__(self):
return str(self.name)
class Products(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="display")
engine = models.CharField(max_length=250, blank=True)
cyl = models.CharField(max_length=250, blank=True)
bore = models.CharField(max_length=250, blank=True)
def __str__(self):
return str(self.engine) + " (ref:" + str(self.ref) + ")"
My views.py:
def Companies(request):
context = {
'categories': Company.objects.all()
}
return render(request, 'product_list.html', context)
My html:
{% for category in categories %}
<h2>{{ category.name }}</h2>
{% for item in category.item_set.all %}
{{ item_engine }}
{% endfor %}
{% endfor %}
only make changes to your HTML file as below and make sure the class name is Product if you are using product_set.all:
{% for category in categories %}
<h2>{{ category.name }}</h2>
{% for item in category.product_set.all %}
{{ item.engine }}
{% endfor %}
{% endfor %}
If still not working then try to remove : + " (ref:" + str(self.ref) + ")"
and also, I think by mistake you have displayed your models.py wrongly. The str functions should be inside the classes like below:
class Company(models.Model):
name = models.CharField(max_length=250)
def __str__(self):
return str(self.name)
class Product(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE,
related_name="display")
engine = models.CharField(max_length=250, blank=True)
cyl = models.CharField(max_length=250, blank=True)
bore = models.CharField(max_length=250, blank=True)
def __str__(self):
return str(self.engine)

Django : render the parent's data to the template only if the child of the child has data

this is my models.py
class Category(models.Model):
name = models.CharField(max_length=50)
ordering_num = models.IntegerField(default=0)
class Meta:
ordering = ['ordering_num']
def __str__(self):
return self.name
class SubCategory(models.Model):
category = models.ForeignKey('Category', on_delete=models.CASCADE)
name = models.CharField(max_length=50)
ordering_num = models.IntegerField(default=0)
class Meta:
ordering = ['ordering_num']
def __str__(self):
return self.name
class ProductBasicModels(models.Model):
whose = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
category = models.ForeignKey(SubCategory, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
standard = models.CharField(max_length=50)
maker = models.CharField(max_length=50, blank=True)
outbox = models.CharField(max_length=50, blank=True)
extra = models.CharField(max_length=100, blank=True)
orderto = models.ForeignKey(OrderCompany, null=True, blank=True, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
views.py
# login_required
def product_list(request):
categories = Category.objects.all()
context = {'categories': categories}
return render(request, 'medicalapp_1/products_h.html', context)
products_h.html
(simple structure...)
...
{% for category in categories %}
{{ category.name }}
{% for sub_category in category.subcategory_set.all %}
{{ sub_category.name }}
{% for list in sub_category.productbasicmodels_set.all %}
{% if list.whose.id is request.user.id %}
{{ list.name }}
{% endif %}
{% endfor %}
{% endfor %}
{% endfor %}
....
according to my code.. all of category and subcategory and products being displayed. But I want to make them display only products models has data.
like this..
category1
subcategory1
product1
product2
product3
category4
subcategory1
product4
product5
subcategory3
product6
(category2,3) and (subcategory2 of category4) are not displayed because they don't have product..
How can I make filter in the view to work like it?
Make use of regroup tag. No need to fetch all categories in the product_list() method. Instead fetch all products (as the name intents)
- with prefetch_related to its foreign key(s), to optimise the query. Then regroup the queryset. That's all!
def product_list(request):
products = ProductBasicModels.objects.all().prefetch_related('category','category__category')
context = {'products': products}
return render(request, 'medicalapp_1/products_h.html', context)
Then, in the template,
{% regroup products by category.category as cats %}
{% for cat in cats %}
<br>{{cat.grouper}}
{% regroup cat.list by category as subcats %}
{% for subcat in subcats %}
<br>{{subcat.grouper}}
{% for product in subcat.list %}
<br>{{product}}
{% endfor %}
{% endfor %}
{% endfor %}
PS :Two separate (& similar) models for category & sub-category is redundant and you shouldn't be using it. You may just use a single model with ForeignKey to 'self'

Django filter multiple foreign key relationships

I want to list all of a teacher's students that have commented on each genre of blog posts. Below is my best effort, but I duplicate student names for each genre, so if they comment on multiple scary blog posts, their names are listed multiple times. How can I list each student name once next to each genre on the teacher profile page?
Models.py
class Genre(models.Model):
name = models.CharField(max_length=200, unique=True)
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='student_profile')
username = models.CharField(max_length=128, unique=True)
teacher = models.ForeignKey('Teacher', blank=True, null=True)
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='teacher_profile')
name = models.CharField(max_length=128, blank=True, unique=True)
class BlogPost(models.Model):
genre = models.ForeignKey(Genre, on_delete=models.CASCADE, null=True)
class Comment(models.Model):
blogpost = models.ForeignKey(BlogPost, related_name='comments', on_delete = models.CASCADE, null=True)
message = CharField(max_length=1000, blank=True)
commenter = models.ForeignKey(User, related_name='comments', on_delete=models.CASCADE, null=True)
Views.py
def teacher_profile(request):
student_list = Student.objects.filter(teacher__user=request.user)
student_blogpost_list = BlogPost.objects.filter(comments__commenter__student_profile__teacher__user=request.user).distinct()
student_genre_list = Genre.objects.filter(blogpost__in=student_blogpost_list).distinct()
return render(
request,
'myapp/teacher_profile.html',
context= {'student_list':student_list, 'student_blogpost_list':student_blogpost_list, 'student_genre_list':student_genre_list},
)
teacher_profile.html
{% if student_genre_list %}
<h4>Genres</h4>
{% for genre in student_genre_list %}
Genre: {{ genre.name }} - Students:
{% for blogpost in genre.blogpost_set.all %}
{% for comment in blogpost.comments.all %}
{% if comment.commenter.student_profile in student_list %}
{{ comment.commenter.student_profile.username }}
{% endif %}
{% endfor %}
{% endfor %}
<br>
{% endfor %}
{% endif %}
I guess it can be done in following way:
student_list = Student.objects.filter(teacher__user=request.user)
student_blogpost_list = BlogPost.objects.filter(comments__commenter__student_profile__teacher__user=request.user).distinct()
data = dict()
for blogpost in student_blogpost_list:
students_usernames = list(blogpost.comment_set.filter(commenter__in=student_list).
values_list('commenter__student_profile__username', flat=True))
if blogpost.genre.name in data:
data[blogpost.genre.name].append(students_usernames)
else:
data[blogpost.genre.name] = students_usernames
for key in data.keys():
data[key] = set(data[key]) # remove duplicates from list
data variable will be equal to something like:
{'genre_1': {'username_1', 'username_2'}, 'genre_2': {'username_2', 'username_3', 'username_4'}}
My point is that there should not be a lot of logic in template. Using this approach you'll just need to iterate over result dictionary.
Nevertheless, I can't guarantee that this code is working, as I didn't set up database with the same scheme.