How to get value of ForeignKey in views.py? - python-3.x

I have below models.py and views.py in Django(This is only a example). How can i print the value of ForeignKey in views.py?
models.py
class student(models.Model):
name = models.CharField(max_length=50)
desc = models.CharField(max_length=100, blank=True, null=True)
phone_number = models.ForeignKey(tell,on_delete=models.SET_NULL, null = True,blank=True)
def __str__(self):
return self.name
class tell(models.Model):
phone_number = models.CharField(max_length=20, blank=True, null=True)
def __str__(self):
return self.phone_number
views.py
phones = student.objects.values('phone_number')
phone = list(phones)
for ob in phone:
print(ob)
This prints only id but i want the value of foreign key.

This is one of the (many) reasons not to use .values(…) [Django-doc]: it erodes the model logical layer of a Django model.
You can work with:
tell.objects.filter(student__isnull=False)
this will retrieve all tell ojects that have at least a related Student.
You can also use .select_related(…) [Django-doc] to select the telephone number while retrieving the students, and thus avoid an N+1 problem:
students = student.objects.select_related('phone_number'):
for stud in students:
print(stud.phone_number)
If you really want to work with the phone_number of the tell model, you can work with .values_list(…) [Django-doc], and work with:
phones = student.objects.values_list('phone_number__phone_number', flat=True)
for phone in phones:
print(phone)
but this are thus not tell objects, and thus updating, refrehing, a record is not possible in that case.

Related

how do I get foreighkey of foreighkey without a loop in django?

It is a Django project, I am trying to create a wishlist (many-to-many will not help because I need DateTime of getting that wished item in the wishlist).
class Client(models.Model):
name = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField()
class WishItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
client = models.ForeignKey(Client, related_name="wishlist", on_delete=models.CASCADE)
added_at = models.DateTimeField(auto_now_add=True)
What I could do is only this:
wishlist = Client.objects.wishlist.select_related('product').all()
wish_products = [item.product for item in wishlist]
But I need something like this, without a loop but with a single SQL query and single line
wishlist = Client.objects.wishlist.product.all()
When I try to run this code I get an error AttributeError: 'RelatedManager' object has no attribute 'product'
You can .filter(…) [Django-doc] and then .order_by(…) [Django-doc] with:
Product.objects.filter(wishitem__client__user=my_user).order_by('wishitem__added_at')
You can make it more covenient to query by spanning a ManyToManyField with your WishItem:
class Client(models.Model):
# …
wishlist = models.ManyToManyField(
'Product',
through='WishItem'
)
class Product(models.Model):
# …
pass
class WishItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
client = models.ForeignKey(Client, related_name='wishitems', on_delete=models.CASCADE)
added_at = models.DateTimeField(auto_now_add=True)
then you can query with:
Product.objects.filter(client__user=my_user).order_by('wishitem__added_at')
It will also make querying for the .wishlist of the Client more covenient, of the Products where the .client_set is a manager that manages the Clients that have that Product on the wishlist.
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.
Many to many relationship will fix the problem you can add extra fields to your WishItem class you can try this :
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField()
class Client(models.Model):
name = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
WishProducts = models.ManyToManyField(Product,through='WishItem')
class WishItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
client = models.ForeignKey(Client, on_delete=models.CASCADE)
added_at = models.DateTimeField(auto_now_add=True)

ManytoManyField Django : how to call models in methods?

I need help for something, I want to call models with ManyToManyField.
I want to have method to get Class A from Class B, and another in Class B to get Class A.
here's my (shortened) code :
class Licence(models.Model):
name = models.CharField(max_length=64)
picture = models.ImageField(upload_to='finder/static/finder/img/licence/',null=True, blank=True)
description = models.TextField(null=True, blank=True)
#returns a list of games from this license
def getGamesOnThisLicence(self):
#i don't know how to proceed
class Game(models.Model):
name = models.CharField(max_length=64)
description = models.TextField()
release_date = models.DateField(null=True, blank=True)
licence = models.ManyToManyField(Licence, blank=True, null=True)
#return name of licence to which the game belongs
def getLicenceName(self):
return self.licence.name
You can access the Games with:
my_license.game_set.all()
so you can use self in the getGamesOnThisLicense, but probably there is not much gain to define a function since this makes accessing the Games already quite convenient.
Perhaps you however want to transform the ManyToManyField into a ForeignKey to License since self.license.name makes not much sense: for a ManyToManyField, self.license is a Manager over License objects that can manage zero, one or more Licenses, so you can not use self.license.name.

How to retrieve all many_to_many relations for each objects in a single query

I have four tables :
class Recipe(models.Model):
item_recipe = models.OneToOneField(Item, on_delete=models.CASCADE, related_name='item_recipe')
items = models.ManyToManyField(Item, through='RecipeItem')
class RecipeItem(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
class Item(models.Model):
def __str__(self):
return self.name
name = models.CharField(max_length=200, unique=True)
effects = ArrayField(models.CharField(max_length=200), blank=True, default=list)
pods = models.IntegerField(null=True)
level = models.IntegerField(null=True)
type = models.CharField(max_length=200, null=True, blank=True, default="Ressource")
image = models.URLField()
class Prix(models.Model):
def __str__(self):
return self.item.name
prix_x1 = models.IntegerField(null=True)
prix_x10 = models.IntegerField(null=True)
prix_x100 = models.IntegerField(null=True)
saved_at = models.DateTimeField()
item = models.ForeignKey(Item, on_delete=models.CASCADE)
A recipe is composed of 1 to 8 Items with a certain quantity indicated is the RecipeItem table.
I would like a query that gives me the price of every Recipe.
In other word, a query that get all items and its prices for every recipe and sum it.
I don't find a way to do that without a for loop.
EDIT
Here is what i have currently, it's not pretty and not effective ..
items = Recipe.objects.all().select_related('item').annotate(
prix1_ressource=Subquery(
Prix.objects.filter(
item=OuterRef('items')
).values('prix_x1').exclude(prix_x1__isnull=True).order_by('-saved_at')[:1]
),
prix1_item=Subquery(
Prix.objects.filter(
item=OuterRef('item_recipe')
).values('prix_x1').exclude(prix_x1__isnull=True).order_by('-saved_at')[:1]
)
).exclude(prix1_item__isnull=True).values('id', 'item_recipe__name', 'prix1_ressource', 'items__name',
'recipeitem__quantity', 'prix1_item',
'item_recipe__type', 'item_recipe')
for id in np.unique(items.values_list('id', flat=True)):
item = items.filter(id=id)
try:
prix_craft = sum([i['recipeitem__quantity'] * i['prix1_ressource'] for i in item])
gain = item[0]['prix1_item'] - prix_craft
except TypeError:
continue
There is really not a nice SQL way. At some point either you or Django will have to iterate over all the items in a query and sum them. Perhaps you could make it a little more efficient by using an Inner join, as documented here, using it to preload the items. But resolving the item's current Prix might require a separate query each time.

SystemCheckError: System check identified some issues:

I am building a project to place an order. When I run makemigrations command then it gives an error of
SystemCheckError: System check identified some issues and I have deleted the migration file from migrations
Traceback:
order.Order.price: (fields.E304) Reverse accessor for 'Order.price' clashes with reverse accessor for 'Order.product'.
HINT: Add or change a related_name argument to the definition for 'Order.price' or 'Order.product'.
order.Order.product: (fields.E304) Reverse accessor for 'Order.product' clashes with reverse accessor for
'Order.price'.
HINT: Add or change a related_name argument to the definition for 'Order.product' or 'Order.price'.
My models.py is as follows:
class Order(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
price = models.ForeignKey(Product, on_delete=models.CASCADE)
def __str__(self):
return self.company + self.product + self.price
Add related_name like:
company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name='company')
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='product')
price = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='price')
related_name is used to link the models. Especially, when you have more than 1 model linking to another model. It is compulsary as in your case. See_this_answer to understand properly about use of related_name.
Both your foreign keys referrencing same model Product without defining different related names:
product = models.ForeignKey(Product, on_delete=models.CASCADE)
price = models.ForeignKey(Product, on_delete=models.CASCADE)
If that's not an error then simply add different related names

Get Foreign Objects instead of QuerySet of List of ID's

single line of code to get the Foreign Key objects instead of QuerySet of UUID's.
I have three Models,
Student, College, Member
Models are defined similar to below
class Student(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name=models.CharField(max_length=128)
class College(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name=models.CharField(max_length=128)
class Member(models.Model):
student= models.ForeignKey(Student, related_name="students", on_delete=models.CASCADE)
college= models.ForeignKey(College, related_name="colleges", on_delete=models.CASCADE)
Where Member contains of Student and College as Foreign Key fields
Now I want to get all the Students who belong to a particular College based on college_id which should be filtered from the Member model
How I am doing now
student_ids = Member.objects.filter(college_id=college_id).values("student")
Now students is a QuerySet of list of UUID's of Student
As I need actual Student objects instead of QuerySet List of UUID's
students = Student.objects.filter(id__in=[str(uid) for uid in student_ids])
I feel this is an improper way. Can anyone suggest a single line of code to get the Foreign Key objects instead of QuerySet of UUID's. Thanks in advance
You can filter with:
Student.objects.filter(students__college_id=college_id)
The related_name=… parameter [Django-doc] however is the name of the relation in reverse. It thus makes more sense to rename these for example to:
class Member(models.Model):
student = models.ForeignKey(
Student,
related_name='members',
on_delete=models.CASCADE
)
college = models.ForeignKey(
College,
related_name='members',
on_delete=models.CASCADE
)
then you filter with:
Student.objects.filter(members__college_id=college_id)
You can also span a conceptual ManyToManyField [Django-doc] between a College and Student:
class Student(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=128)
colleges = models.ManyToManyField(
'College',
related_name='students'
through='Member'
)
Then you can filter with:
Student.objects.filter(colleges=college_id)

Resources