Django - Grouping 2 context datasets into 1 forloop - python

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

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 iterate over a list that is an item of a queryset

I have a list that is generated by a method on one of my models. On the home page it works wonderfully, however when I go to a detail view of one project I can access all the parts of that project as they are direct fields of the Model, but I can't access the items in the list.
Model:
class Project(models.Model):
date_published = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
area = models.ForeignKey(Area, on_delete=models.PROTECT)
title = models.CharField(max_length=128, unique=True)
slug = models.SlugField(max_length=64)
summary = models.CharField(max_length=256)
others = models.CharField(max_length=128, blank=True)
deadline = models.DateField(null=True, blank=True)
priority = models.ForeignKey(Priority, on_delete=models.PROTECT)
closed = models.DateTimeField(null=True, blank=True)
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Project, self).save(*args, **kwargs)
#property
def updates(self):
updates = []
sequence_id = 1
categories = set(self.update_set.all().values_list(
'category__id', flat=True))
for cat_id in categories:
a = Update.objects.filter(
project=self, category__id=cat_id).order_by('added').last()
if cat_id == sequence_id:
updates.append(a)
else:
for i in range(cat_id - sequence_id):
updates.append('null')
updates.append(a)
sequence_id = cat_id
sequence_id += 1
return updates
class Update(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
category = models.ForeignKey(UpdateCategory, on_delete=models.PROTECT)
update = models.TextField(max_length=240, blank=True)
added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.update
The view is simple:
class ProjectDetailView(DetailView):
template_name = 'project_portal/project_detail.html'
queryset = Project.objects.all()
and here is the dynamic url that I am using:
path('project/<int:pk>/',
ProjectDetailView.as_view(), name='project_detail'),
As for the template, I'm lost, here is one of the things I have tried:
<!DOCTYPE html>
{% extends "project_portal/base.html" %}
{% block home %}
<div id="main">
<div id="content">
<div>
<h1>{{ object.title }}</h1>
<h1>hello</h1>
{% if object_list %}
{% for item in updates %}
<p>{{ item }}</p>
{% endfor %}
{% else %}
<h2>No records found for this project</h2>
{% endif %}
</div>
</div>
</div>
{% endblock %}
What do I need to do to access the "updates" list that gets generated?
update is a property of the model instance, you need to access it from there like any other attribute. Also note, there is no object_list in a detail view.
<div>
<h1>{{ object.title }}</h1>
<h1>hello</h1>
{% for item in object.updates %}
<p>{{ item }}</p>
{% endfor %}
</div>

Display relationship attribute in django template

So, in my models I have:
class Ingredient(models.Model):
name = models.CharField(max_length=200)
articleNumber = models.IntegerField(unique=True)
costPerUnity = models.DecimalField(max_digits=4, decimal_places=2)
class Recipe(models.Model):
name = models.CharField(max_length=200)
ingredients = models.ManyToManyField(Ingredient, through='Recipe_Ingredient', related_name='recipes')
class Recipe_Ingredient(models.Model):
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
ingredient = models.ForeignKey(Ingredient, on_delete=models.CASCADE)
quantity = models.FloatField()
GRAM = 'g'
KILOGRAM = 'kg'
LITER = 'l'
CENTILITER = 'cl'
UNITY_CHOICES = (
(GRAM, 'Gram(s)'),
(KILOGRAM, 'Kilogram(s)'),
(LITER, 'Liter(s)'),
(CENTILITER, 'Centiliter(s)'),
)
quantityUnit = models.CharField(
max_length=2,
choices=UNITY_CHOICES,
default=GRAM,
)
In my template:
{% for ingredient in recipe.ingredients.all %}
<li>{{ ingredient.name }} - # quantity goes here </li>
{% endfor %}
How can I show the quantity atribute of the Recipe_Ingredient associated with this recipe and ingredient?
In the shell I could do this query: Recipe_Ingredient.objects.get(ingredient=Ingredient.objects.get(name='Cenoura'), recipe=Recipe.objects.get(name='Teste')), but I'm not quite sure how to do it in the template and what's the correct way of doing it.
You can accomplish what you want by iterating over the Receipe_Ingredient relationship.
{% for recipe_ingredient in recipe.recipe_ingredient_set.all %}
<li>{{ recipe_ingredient.ingredient.name }} - {{ recipe_ingredient.quantity }} </li>
{% endfor %}

How to enroll custom user as students in django

I have django course model and custom user as students
I added some course for logged student, Now i need to show the profile of user or student and his course in student_profile.html
here is my model for course
class Course(models.Model):
students = models.ManyToManyField('Profile', blank=True)
Course_Name = models.CharField(max_length=200)
Duration_Time = models.CharField(max_length=50)
Course_Fee = models.IntegerField()
Discount_Fee = models.IntegerField()
Course_Image = models.FileField()
Course_Description = models.TextField(max_length=500)
def __str__(self):
return self.Course_Name + ' - ' + self.Duration_Time
here is model profile
class Profile(models.Model):
STUDENT = 1
TEACHER = 2
ROLE_CHOICES = (
(STUDENT, 'Student'),
(TEACHER, 'Teacher'),
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone_number = models.CharField(max_length=30, blank=False, help_text='Required.')
email_confirmed = models.BooleanField(default=False)
role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, null=True, blank=True)
# this method called for admin panel
class Meta:
verbose_name = 'profile'
verbose_name_plural = 'profiles'
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
here is my students profile template
{% if user.is_authenticated %}
<p>welcome {{ user.first_name }} --
{% for course in user.profile.course_set.all %}
{{ course.name }}{% if not forloop.last %},{% endif %}
{% empty %}
<span>No course</span>
{% endfor %}
</p>
{% endif %}
<p>Logout</p>
When student login in profile template showing only studentname
Here is result in browser
welcome studnet for course -- ,
Logout
You can add a ManyToManyField in your Course Model, named:
class Course(models.Model):
# other fields
students = models.ManyToManyField('Profile',blank=True)`
So when you have a profile instance, this query profile_instance.course_set.all() will display all the courses that this profile/student has attended
In your view, don't override get_queryset()
{% if user.is_authenticated %}
<p>welcome {{ user.first_name }} --
{% for course in user.profile.course_set.all %}
{{ course.Course_Name }}{% if not forloop.last %},{% endif %}
{% empty %}
<span>No course</span>
{% endfor %}
</p>
{% endif %}
<p>Logout</p>
...

Retrieve a filtered ForeignKey with Django

I have three main models, Picture, Place and PlaceRating:
class Picture(models.Model):
file = ImageField(max_length=500, upload_to="images")
user = models.ForeignKey(User, null=True, related_name="userpictures")
place = models.ForeignKey(Place, null=True, related_name='pictures')
class PlaceRating(models.Model):
place = models.ForeignKey(Place, null=True, related_name="placeratings")
user = models.ForeignKey(User, null=True, related_name="userratings")
rating = models.DecimalField(null=True, max_digits=4, decimal_places=1)
class Place(models.Model):
name = CharField(max_length=50)
I would like to display the place's rating given by the user, together with the place's image, but I cannot manage to do that as I would need to filter the ForeignKey and Django does not seem to allow that.
Example of what I would like to do:
View:
pictures = Picture.objects.filter(user=request.user)
Template:
{% for picture in pictures %}
<img src="{{ picture.file.url }}" class="bigpicture">
{{ picture.place.placeratings.0.rating|user:picture.user }}
{% endfor %}
For information, I managed to do it with templatetags, but this generates a lot of different queries to the database which I can't prefetch..:
{% for picture in pictures %}
<img src="{{ picture.file.url }}">
{% getplaceratingrelatedtopic picture.place.id picture.user.id %}
{% endfor %}
And:
#register.simple_tag
def getplaceratingrelatedtopic(placeid, userid):
print(placeid)
theplace = Place.objects.get(id=placeid)
user = User.objects.get(id=userid)
placerating = PlaceRating.objects.filter(author=user, place=place).last()
if rating:
return placerating.rating
else:
return ""
I work with Python 2.7/Django 1.9.
Any clue ? Thanks a lot!
You should move the logic to the model and use it in the template.
class Picture(models.Model):
file = ImageField(max_length=500, upload_to="images")
user = models.ForeignKey(User, null=True, related_name="userpictures")
place = models.ForeignKey(Place, null=True, related_name='pictures')
class PlaceRating(models.Model):
place = models.ForeignKey(Place, null=True, related_name="placeratings")
user = models.ForeignKey(User, null=True, related_name="userratings")
rating = models.DecimalField(null=True, max_digits=4, decimal_places=1)
class Place(models.Model):
name = CharField(max_length=50)
#property
def rating(self):
return self.placeratings.get().rating
{% for picture in picture_list %}
<img src="{{ picture.file.url }}" class="bigpicture">
{{ picture.place.rating }}: {{ picture.user.username }}
{% endfor %}

Categories