How to query a model made up of multiple Foreign Keys - python

I'm doing the CS50 Web programming course, learning to use Django. The learning exercise basically has you recreating this pizza restaurant menu with django.
I've created some models for the data, and now I'm trying to use Django templating to create a menu page for the pizzas.
Here are my models:
from django.db import models
class intToppings(models.Model):
name = models.CharField(max_length=16)
intToppings = models.IntegerField() # 0 Cheese, 1/2/3, 5 Special
def __str__(self):
return f"{self.name}"
class Size(models.Model):
size = models.CharField(max_length=16)
def __str__(self):
return f"{self.size}"
class PizzaBase(models.Model):
base = models.CharField(max_length=16)
def __str__(self):
return f"{self.base}"
class Toppings(models.Model):
topping = models.CharField(max_length=32)
def __str__(self):
return f"{self.topping}"
class Pizza(models.Model):
size = models.ForeignKey(Size, on_delete=models.CASCADE) # CASCADE will delete all Regular Pizzas if their size is deleted (as opposed to .SET_NULL)
base = models.ForeignKey(PizzaBase, on_delete=models.CASCADE)
intToppings = models.ForeignKey(intToppings, on_delete=models.CASCADE)
price = models.IntegerField() # price in cents
def __str__(self):
return f"{self.size} {self.base} {self.intToppings} Pizza"
There's an entry "small" and "large" in the Size db, and for intToppings there is one entry with name "cheese" and an int of 0, another for "1 topping" with an int of 1, etc.
And for the Pizza model, I made an entry for every combo on the menu, ie:
<QuerySet [<Pizza: small Regular Cheese Pizza>, <Pizza: small Regular 1 Topping Pizza>, <Pizza: small Regular 2 Toppings Pizza>, ...
... <Pizza: large Sicilian 2 Toppings Pizza>, <Pizza: large Sicilian 3 Toppings Pizza>, <Pizza: large Sicilian Special Pizza>]>
On my views.py, I can't really pass that whole data set to the django template because it's not sensible/possible to loop through it to create an html table. (my html table is identical to the one on their website, one table for regular pizza, one for sicilian.)
I'm trying to solve this issue by first constructing a list/array or dict object that will pass data to the django template in a structure that is easy to loop through. And to do this I want to query the Pizza model.
Essentially, all I'm looking to do is (pseudo code: SELECT Pizza WHERE size="small" base="Regular", intToppings=0 and get the price for that pizza.
I don't seem to be able to query the foreign keys though;
Pizza.objects.all().filter(price=1220)
works but isn't what I need. What I need is;
p = Pizza.objects.all().filter(base="Regular", size="small", intToppings=0)
print(p.price)
which doesn't work.

Have you tried to use the field names of the related models? Like this:
p = Pizza.objects.filter(
base__base="Regular",
size__size="small",
intToppings__intToppings=0)
print(p)
Like the docs say,
you first access the related model (say base) and then you access the field of that related model (__base) and compare that to the string you want, resulting in base__base='something'.
Maybe you even could rename the field PizzaBase.base to PizzaBase.name to make it less confusing.

Try this:
p = Pizza.objects.filter(
base__base = "Regular",
size__size = "small",
intToppings_id = 0,
)
Note that I changed intToppings to intToppings_id. If you need to filter by a foreign key, you can pass in the intToppings object, or you can add _id to the end of the column name and simply insert the pk value.

Related

Django chain multiple queries in view

I have three models:
Course
Assignment
Term
A course has a ManyToManyField which accesses Django's default User in a field called student, and a ForeignKey with term
An assignment has a ForeignKey with course
Here's the related models:
class Assignment(models.Model):
title = models.CharField(max_length=128, unique=True)
points = models.IntegerField(default=0, blank=True)
description = models.TextField(blank=True)
date_due = models.DateField(blank=True)
time_due = models.TimeField(blank=True)
course = models.ForeignKey(Course)
class Course(models.Model):
subject = models.CharField(max_length=3)
number = models.CharField(max_length=3)
section = models.CharField(max_length=3)
professor = models.ForeignKey("auth.User", limit_choices_to={'groups__name': "Faculty"}, related_name="faculty_profile")
term = models.ForeignKey(Term)
students = models.ManyToManyField("auth.User", limit_choices_to={'groups__name': "Student"}, related_name="student_profile")
When a user logs in to the page, I would like to show them something like this bootstrap collapse card where I can display each term and the corresponding classes with which the student is enrolled.
I am able to access all of the courses in which the student is enrolled, I'm just having difficulty with figuring out the query to select the terms. I've tried using 'select_related' with no luck although I may be using it incorrectly. So far I've got course_list = Course.objects.filter(students = request.user).select_related('term'). Is there a way to acquire all of the terms and their corresponding courses so that I can display them in the way I'd like? If not, should I be modeling my database in a different way?
https://docs.djangoproject.com/en/1.11/ref/models/querysets/#values
You could use values or values_list here to get the fields of the related model Term.
For example expanding on your current request:
To retrieve all the Terms' name and duration for the Courses in your queryset
Course.objects.filter(students = request.user).values('term__name', 'term__duration')
I am not sure what the fields are of your Term model, but you would replace name or duration with whichever you are trying to get at.
I think it helps you
terms = Terms.objects.filter(....) # terms
cources0 = terms[0].course_set.all() # courses for terms[0]
cources0 = terms[0].course_set.filter(students=request.user) # courses for terms[0] for user

Linking one models with a set of another in Django

I am currently working on developing a database and API system where users can create a portfolio which contains a list of coins. I am using Django and I searched everywhere but I kept seeing foreign keys but I'm not sure that's what I need in this situation.
I want two models, one for portfolios which a user will be able to query on, and another coin model which the user will be able to also query on. However in the portfolio there should be a list of coins. I know how to do this in Java using objects but not sure the method in Django.
Here is my model class:
from django.db import models
class Portfolio(models.Model):
name = models.CharField(max_length=250)
def __str__(self):
return self.name
class Coin(models.Model):
name = models.CharField(max_length=100)
symbol = models.CharField(max_length=5)
price = models.DecimalField(max_digits=20, decimal_places=9)
info = models.TextField()
website = models.TextField()
rank = models.IntegerField()
def __str__(self):
return self.name + " - " + self.symbol
Now I would ideally have something like coins = list of Coins model if I was using java to make the objects, but since this is for a database and in Django I'm not sure how I should link the two.
I've seen related objects but did not understand the explanations for my issue. How should I go about setting up these models? Thanks.
It sounds like you want to have a number of Portfolio objects each of which can have varying investments in Coin objects. In this case, you'd want to use a ManyToManyField:
class Portfolio(models.Model):
name = models.CharField(max_length=250)
coins = models.ManyToManyField(Coin)
The database would then store the two dimensional table of which Portfolio holds which coin.
However an alternate approach you could try is to create an object that separately represents the investment:
class Investment(models.Model):
portfolio = models.ForeignKey(Portfolio)
coin = models.ForeignKey(Coin)
bought = models.DateTimeField() # date the investment was made
sold = models.DateTimeField() # date the investment was sold
amount = models.DecimalField() # number of coins held
You could then add a property to Portfolio:
class Portfolio(models.Model):
name = models.CharField(max_length=250)
#property
def coins(self):
return Investment.objects.filter(portfolio=self)
In this way you can not only keep track of which portfolio holds which coins, buy also the entire historical positions too.

How to write a complex Mysql Query in Django

I am new to Django and I am working on a small module of a Django application where I need to display the list of people who have common interest as that of any particular User. So Suppose if I am an user I can see the list of people who have similar interests like me.
For this I have 2 models :
models.py
class Entity(models.Model):
name = models.CharField(max_length=255, unique=True)
def __str__(self):
return self.name
class UserLikes(models.Model):
class Meta:
unique_together = (('user', 'entity'),)
user = models.ForeignKey(User)
entity = models.ForeignKey(Entity)
def __str__(self):
return self.user.username + " : " + self.entity.name
So in the Entity Table I store the Entities in which user can be interested Eg : football, Music, Code etc.
and in the UserLikes I store the relation about which user likes which entity.
Now I have a Query to fetch details about which user has maximum interest like any particular user :
SELECT y.user_id, GROUP_CONCAT(y.entity_id) likes, COUNT(*) total
FROM likes_userlikes x
JOIN likes_userlikes y ON y.entity_id = x.entity_id AND y.user_id <> x.user_id
WHERE x.user_id = ?
GROUP BY y.user_id
ORDER BY total desc;
Problem is how do I write this Query using Django Querysets and change it into a function.
# this gives you what are current user's interests
current_user_likes = UserLikes.objects.filter(user__id=user_id) \
.values_list('entity', flat=True).distinct()
# this gives you who are the persons that shares the same interests
user_similar_interests = UserLikes.objects.filter(entity__id__in=current_user_likes) \
.exclude(user__id=user_id) \
.values('user', 'entity').distinct()
# finally the count
user_similar_interests_count = user_similar_interests.count()
Here the user_id is the user's id you want to query for.
One advice though, it's not good practice to use plural form for model names, just use UserLike or better, UserInterest for it. Django would add plural form when it needs to.

Sorting ManyToMany field automatically in Django model

I currently have a set of models that look similar to this contrived code:
class Pizza(models.Model):
price = models.FloatField()
topping = models.ManyToManyField(Topping, through="PizzaToppings")
class Topping(models.Model):
name = models.CharField(max_length=50)
class PizzaToppings(models.Model):
class Meta:
ordering=["order_to_add_topping"]
pizza = models.ForeignKey(Pizza)
topping = models.ForeignKey(Topping)
order_to_add_topping = models.IntegerField()
My problem is that what happens when I attempt to access the toppings of a pizza in the order specified in the PizzaToppings ManyToMany extra fields table. Assume the pizza has cheese and ham, with the order_to_add_topping in the PizzaToppings data set to 0 and 1 respectively:
>>> pizza = Pizza.objects.get(pk=490)
>>> pizza.toppings.all()[0].name
'Ham'
That should say 'Cheese'. I would have thought the RelationManager would have respected the ordering Meta class field, but it appears it doesn't. So I guess accessing the name of the first topping added to the pizza shouldn't be done with pizza.toppings.all()[0].name.
How should it be accessed? Is the problem with my model query or is it how I have my models set up?
Your model is fine you just need to query the relationship since now you have a "through" relationship with extra fields. The relationship is created automatically as topping_relationship in your case, so your query should be:
pizza.toppings.order_by('topping_relationship__order_to_add_topping')

manyToManyField question

Hay guys, I'm writing a simple app which logs recipes.
I'm working out my models and have stumbled across a problem
My Dish models needs to have many Ingredients. This is no problem because i would do something like this
ingredients = models.ManyToManyfield(Ingredient)
No problems, my dish now can have many ingrendients.
However, the problem is that the ingredient needs to come in different quantities.
I.E 4 eggs, 7 tablespoons sugar
My Ingredient Model is very simple at the moment
class Ingredient(models.Model):
name = models.TextField(blank=False)
slug = models.SlugField(blank=True)
How would i go about work out this problem? What fields would i need to add, would i need to use a 'through' attribute on my ManyToManyfield to solve this problem?
I think you got the right answer with a "through" table ( http://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany )
Model
class Recipe(models.Model):
name = models.TextField(blank=False)
ingredients = models.ManyToManyField(Ingredient, through='Components')
class Ingredient(models.Model):
name = models.TextField(blank=False)
slug = models.SlugField(blank=True)
class Components(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
quantity = models.DecimalField()
You can put unit of quantity (gram, kilo, tablespoon, etc) on Ingredient level, but I think it is better on Ingredients level (for example you can have 1 recipe with 10 Cl of milk but one other with 1L ... So "different" units for a same ingredient.
Data Creation
By Dish you mean Recipe right ? If you have a look to previous link (http://docs.djangoproject.com/en/dev/topics/db/models/#intermediary-manytomany), they give you a good example (based on the beatles).
Basically :
1.Create a Recipe:
cake=Recipe.objects.create(name="Simple Cake")
2.Create several Ingredient (if they doesn't already exist from a previous recipe ;)):
egg = Ingredient.objects.create(name="Egg")
milk = Ingredient.objects.create(name="milk")
3.Create the relationship:
cake_ing1 = Components.objects.create(recipe=cake, ingredient=egg,quantity = 2)
cake_ing2 = Components.objects.create(recipe=cake, ingredient=milk,quantity = 200)
and so on. Plus, I'm now quite sure that unit should go to Components level, with a default unit as "piece" (that would be for yours eggs ...), and would be something like "mL" for milk.
Data Access
In order to get ingredients (Components) of a recipe just do :
cake = Recipe.objects.get(name = "Simple Cake")
components_cake = Components.objects.get(recipe = cake)

Categories