How can I access a specific field of a queryset in Django? - python

I have these models in my Django app:
class Book(models.Model):
title = models.CharField(max_length=50, unique=True)
owner = models.CharField(max_length=30)
price = models.DecimalField(max_digits=5, decimal_places=2, null=True)
book_rating = models.ForeignKey('Rating', null=True)
RATE_CHOICES = zip(range(1,6), range(1,6))
class Rating(models.Model):
user = models.ForeignKey(User)
this_book = models.ForeignKey(Book)
rate = models.DecimalField(max_digits=2, decimal_places=1, choices=RATE_CHOICES)
comment = models.TextField(max_length=4000, null=True)
I am trying to access the Ratings of each instance of the Book model. Here is what I've tried so far in the shell:
from django.contrib.contenttypes.models import ContentType
>>> ctype = ContentType.objects.get_for_model(Rating)
>>> ctype
<ContentType: rating>
>>> book_titles = ctype.model_class().objects.filter(this_book__title='My Test Book')
>>> book_titles
<QuerySet [<Rating: My Test Book - parrot987 - 3.0>, <Rating: My Test Book - 123#gmail.com - 5.0>]>
How can I access the two rating values of each object (5.0 and 3.0) without all of this other data?
Can this be done in such a way that I am able to average the numbers and return the final value?

For 1. you can use (relevant documentation):
Rating.objects.filter(this_book__title='My Test Book').values('rate')
If you just want a flat list you can use values_list('rate', flat=True) instead of values('rate').
For 2 (relevant documentation):
from django.db.models import Avg
Rating.objects.filter(this_book__title='My Test Book').aggregate(Avg('rate'))
This will return a dictionary where the key is rate__avg and the value is the average of the ratings.

Please see the following for Many to One fields django - Get the set of objects from Many To One relationship
To access the rating, you can use a for loop and access the individual values e.g.
total = 0
for rating in book_titles.book_set.all()
total += rating.rate
Good luck!

Related

Django - SubQuery e function F()

I have 2 models (author and book). I want to annotate in the author model the highest book price for every authors (in order to manipulate it later). I would like to make a query with an internal sub-query using the F() function but the problem is that the F() is refered to the book model but I would like to refer it to the author model. I'll show that with an example. The models are shown below:
class author:
authorID = models.AutoField(primary_key = True, auto_created = True, unique = True)
name = models.CharField(max_length=100)
surname = models.CharField(max_length=100)
class book:
ownerID = models.ForeignKey(author, on_delete = models.CASCADE, related_name='hook')
title = models.CharField(max_length=100)
price = models.IntegerField()
The query in question is as follows:
value = author.objects.annotate(maxPrice = (book.objects.filter(ownerID = F('authorID')).order_by('-price') [0]))
F('authorID') is refered to the book DB and so Django returns an error but I want to refer it to the author DB to achieve my goal. How can I do that? Thanks

How to run a model's function with DTL?

I am fairly new to django and I am trying the following:
I am making an ordering web app for a nearby business. I am trying to add all the calories in a combo.
I have a model "Consumable", this represent the different food and drinks there are.
Here is the code for the "Consumable" Model:
class Consumable(models.Model):
name = models.CharField(max_length=80, unique=True)
category = models.ForeignKey(FoodCategory, null=True, on_delete=models.SET_NULL)
price= models.FloatField(default=0.00)
calories = models.IntegerField(blank=False)
image = models.ImageField(upload_to="images/Food/")
description = RichTextField(blank=True, max_length=500)
restaurant = models.ForeignKey(Place, on_delete=models.CASCADE)
added = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
Then, I have a model called "Meal" which has the following code:
class Meal(models.Model):
name = models.CharField(max_length=100)
image = models.ImageField(upload_to='images/photos/meals')
consumables = models.ManyToManyField(Consumable, related_name="consumable")
restaurant = models.ForeignKey(Place, on_delete=models.CASCADE)
price = models.FloatField()
def add_cals(meal_consumables):
total_calories = 0
for x in meal_consumables.values():
global total_calories += float(x.calories)
return total_calories
I am trying to call this model's function, add_cals from my HTML file using the DTL (Django Template Language).
For example, if you have a model stored with the variable x in your view, and it is in the view's context dictionary, you could simply call the model by using <p>{{x}}</p>, for example. And if the model has a variable x1, you could call it as <p>{{x.x1}}</p> and it works fine. However, if the x model from my example has a function
add_x(x):
return x
then it wouldn't work when you call it as <p>{{x.add_x(1)}}</p> for some reason. Could some wise individual out there in this world be so humble to share his or her knowledge with me in order to help me?
Thanks to everyone who tries.

Realizing rating in django

What is a better way of realizing rate field in model. Now I have this one:
class Story(models.Model):
...
rate = models.(help here)
class Rating(models.Model):
rate = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(10.0)])
story = models.ForeignKey(Story, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
Or there is another way of doing this?
As #Liudvikas Bajarunas said, it's enough to define story as a foreign key on the Rating model. You can access the story ratings using rating_set:
story_ratings = story.rating_set.all()
See the documentation on following relationships backwards for more info.
You can combine that approach with aggregation to get the average rating of a story:
class Story(models.Model):
...
#property
def average_rating(self):
return self.rating_set.all().aggregate(Avg('rate'))['rate__avg']
There are some improvements that you can make:
It is better to refer to the user model with the AUTH_USER_MODEL setting [Django-doc] to refer to the user model, since you can later change your mind about it;
You probably want to make user and story unique together, such that a user can not make two ratings for the same story;
some databases, like PostgreSQL allow us to enforce range constraints at the database level, and thus make it more safe.
we thus can rewrite this to:
from django.conf import settings
from django.db import models
from django.db.models import CheckConstraint, Q, UniqueConstraint
class Rating(models.Model):
rate = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(10.0)])
story = models.ForeignKey(Story, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
class Meta:
constraints = [
CheckConstraint(check=Q(rate__range=(0, 10)), name='valid_rate'),
UniqueConstraint(fields=['user', 'story'], name='rating_once')
]
You should either go with a through field like this:
class Story(models.Model):
rates = models.ManyToManyField(User, through=Rating)
class Rating(models.Model):
rate = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(10.0)])
story = models.ForeignKey(Story, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
or you can do it your way with a separate model which in this case your either should remove the rate field from Story model or remove the story field from Rating model:
class Story(models.Model):
...
# rate = models.(help here) No need anymore
class Rating(models.Model):
rate = models.FloatField(validators=[MinValueValidator(0.0), MaxValueValidator(10.0)])
story = models.ForeignKey(Story, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
and your queryset will be something like this:
story.rating_set.all()
Which will include all the ratings for the selected story instance.

Django Cannot assign "'Pizza'": "Order.Food_Name" must be a "Foods" instance

Hello Guys I am working on a restaurant project which allow user to select food item and book an order but i am getting this error as i try to book an order
"Django Cannot assign "'Pizza'": "Order.Food_Name" must be a "Foods" instance."
I am using drop down menu to select food items i am using django version 2.1.5 . Please Help
views.py
def place_order(request):
name = request.POST["user"]
food_items = request.POST['food_item']
qty = request.POST['qty']
rating = request.POST['ratings']
price = Foods.Food_Price
order = Order(Date=datetime.date, Name_of_Person=name,Food_Name=food_items, Qty=qty, Total=price, Ratings=rating)
order.save()
return render(request, "index.html")
model.py
from django.db import models
class Foods(models.Model):
Food_Number = models.IntegerField(null=False,)
Food_Name = models.CharField(max_length=30, primary_key=True, null=False)
Food_Qty = models.CharField(max_length=10)
Food_Price = models.IntegerField()
def __str__(self):
return f"{self.Food_Number} - {self.Food_Name} {self.Food_Price}"
class Order(models.Model):
Order_id = models.AutoField(null=False, primary_key=True)
Date = models.DateField()
Name_of_Person = models.CharField(null=False, max_length=40)
Food_Name = models.ForeignKey(Foods, on_delete=models.CASCADE)
Qty = models.CharField(max_length=10)
Total = models.IntegerField()
Ratings = models.IntegerField()
def __str__(self):
return f"{self.Order_id} - {self.Name_of_Person} |{self.Food_Name} |{self.Total}"
What can i do solve this error
Problem is in your Order model Food_Name is foreign-key field. So you need to assign model-instance which is Food in this case to this field. But you are assigning food_items = request.POST['food_item'] which is suppose to be food_name string i guess. That is why this error raise. I don't think your model is properly design. Food_Name is not an unique id field in Food model rather in your Order table you would like to have Food not Food_name.

Django group up by foreign keys

So i have two models:
class Product(models.Model):
name = models.CharField(_('Name'), max_length=200)
class Like(models.Model):
product = models.ForeignKey(Product, related_name='likes', on_delete=models.DO_NOTHING)
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name='likes', on_delete=models.DO_NOTHING)
What I want is to get the total number of base products (<5 likes), standard products (<10 likes) and premium products.
I don't know how I should count FK of each product and then group them; and preferably in 1 query.
python 3.6, django 2.1, posgtresql
I don't understand how that should be acomplished in a single query, but you could try this:
from django.db.models import Count
qs = Product.objects.annotate(num_likes=Count('like'))
base_products = qs.filter(num_likes__lt=5)
standard_products = qs.filter(num_likes__gte=5, num_likes__lt=10)
premium_products = qs.filter(num_likes__gte=10)
This code gives you 3 QuerySet objects with the data you asked for.
If you want a single database query (it still includes both tables in the query) but don't mind loading the data in python lists instead of querysets (needs more memory and maybe even more time), then you could try:
from django.db.models import Count
qs = Product.objects.annotate(num_likes=Count('like'))
base_products = []
standard_products = []
premium_products = []
for p in qs:
if p.num_likes < 5:
base_products.append(p)
elif p.num_likes < 5:
standard_products.append(p)
else:
premium_products.append(p)

Categories