Access foreign key objects in Django - django

I have two models:
class Item(models.Model):
name = models.CharField()
class Param(models.Model):
product = models.ForeignKey(Item, related_name="param")
height = models.IntegerField()
price = models.IntegerField()
I want is to annotate item with average price, but only keep params with height > 60.
items = Item.objects.filter(param__height__gt=60).annotate(price=Avg('param__price'))
That's where I have problems. I want to fetch filtered Param objects from above query.
Is it possible to do?
I know that there is workaround:
for item in items:
item.params = item.param.filter(height__gt=60)
But there are a lot of additional queries.
So, my question whether can I access filtered param objects from items?

items = Item.objects.select_related('param').filter(param__height__gt=60).annotate(price=Avg('param__price')).all()

class Artist():
blah = models.TextField()
class Album()
blah = models.ForeignKey(blah)
the key is to be shaed in the class like this^
Also for example the key can be x,y points, time, user, or whatever you want!
https://github.com/Ry10p/django-Plugis/blob/master/courses/models.py
example line 52
-Cheers

Related

how to select fields in related tables quickly in django models

I'm trying to get all values in current table, and also get some fields in related tables.
class school(models.Model):
school_name = models.CharField(max_length=256)
school_type = models.CharField(max_length=128)
school_address = models.CharField(max_length=256)
class hometown(models.Model):
hometown_name = models.CharField(max_length=32)
class person(models.Model):
person_name = models.CharField(max_length=128)
person_id = models.CharField(max_length=128)
person_school = models.ForeignKey(school, on_delete=models.CASCADE)
person_ht = models.ForeignKey(hometown, on_delete=models.CASCADE)
how to quick select all info i needed into a dict for rendering.
there will be many records in person, i got school_id input, and want to get all person in this school, and also want these person's hometown_name shown.
i tried like this, can get the info i wanted. And any other quick way to do it?
m=person.objects.filter(person_school_id=1)
.values('id', 'person_name', 'person_id',
school_name=F('person_school__school_name'),
school_address=F('person_school__school_address'),
hometown_name=F('person_ht__hometown_name'))
person_name, person_id, school_name, school_address, hometown_name
if the person have many fields, it will be a hard work for list all values.
what i mean, is there any queryset can join related tables' fields together, which no need to list fields in values.
Maybe like this:
m=person.objects.filter(person_school_id=1).XXXX.values()
it can show all values in school, and all values in hometown together with person's values in m, and i can
for x in m:
print(x.school_name, x.hometown_name, x.person_name)
You add a prefetch_related query on top of your queryset.
prefetch_data = Prefetch('person_set, hometown_set, school_set', queryset=m)
Where prefetch_data will prepare your DB to fetch related tables and m is your original filtered query (so add this below your Person.objects.filter(... )
Then you do the actual query to the DB:
query = query.prefetch_related(prefetch_data)
Where query will be the actual resulting query with a list of Person objects (so add that line below the prefetch_data one).
Example:
m=person.objects.filter(person_school_id=1)
.values('id', 'person_name', 'person_id',
school_name=F('person_school__school_name'),
school_address=F('person_school__school_address'),
hometown_name=F('person_ht__hometown_name'))
prefetch_data = Prefetch('person_set, hometown_set, school_set', queryset=m)
query = query.prefetch_related(prefetch_data)
In that example I've broken down the queries into more manageable pieces, but you can do the whole thing in one big line too (less manageable to read though):
m=person.objects.filter(person_school_id=1)
.values('id', 'person_name', 'person_id',
school_name=F('person_school__school_name'),
school_address=F('person_school__school_address'),
hometown_name=F('person_ht__hometown_name')).prefetch_related('person, hometown, school')

How do I construct an order_by for a specific record in a ManyToOne field?

I'm trying to sort (order) by statistical data stored in a ManyToOne relationship. Suppose I have the following code:
class Product(models.Model):
info = ...
data = models.IntegerField(default=0.0)
class Customer(models.Model):
info = ...
purchases = models.ManyToManyField(Product, related_name='customers', blank=True)
class ProductStats(models.Model):
ALL = 0
YOUNG = 1
OLD = 2
TYPE = ((ALL, 'All'), (YOUNG, 'Young'), (OLD, 'Old'),)
stats_type = models.SmallIntegerField(choices=TYPE)
product = models.ForeignKey(Product, related_name='stats', on_delete=models.CASCADE)
data = models.FloatField(default=0.0)
Then I would like to sort the products by their stats for the ALL demographic (assume every product has a stats connected to it for ALL). This might look something like the following:
products = Product.objects.all().order_by('stats__data for stats__stats_type=0')
Currently the only solution I can think of is either to create a new stats class just for all and use a OneToOneField for Product. Or, add a OneToOneField for Product pointing to the ALL stats in ProductStats.
Thank you for your help.
How about like this using multiple fields in order_by:
Product.objects.all().order_by('stats__data', 'stats__stats_type')
# it will order products from stats 0, then 1 then 2
Or if you want to get data for only stats_type 0:
Product.objects.filter(stats__stats_type=0).order_by('stats__data')
You can annotate the value of the relevant demographic and order by that:
from django.db.models import F
Product.objects.all().filter(stats__stats_type=0).annotate(data_for_all=F('stats__data').order_by('data_for_all')

Django Accessing the attributes of an M2M field by querying the related table

I have a table which looks like this
class Person(User):
"""
This model represents person's personal and
professional details.
"""
attribute1 = models.CharField(max_length=100)
attribute2 = models.TextField()
attribute3 = models.TextField()
attribute4 = models.ForeignKey(Receptionist)
referred_attribute1 = models.ManyToManyField(Hobby)
class Hobby(models.Model):
name = models.CharField(max_length=100)
Use case:
A Person P1 has Hobbies h1,h2,h3(defined[as 3 separate entries] in table Hobby).
Now I want to retrieve a Person object with all the attributes and properties that it has including Hobbies. For that I'm executing the following :
Person.objects.values("attribute1",
"attribute2",
"referred_attribute1").get(attribute3="p1's attribute")
What I want is :
{'attribute1':'p1_attribute1',
'attribute2':'p1_attribute2',
'referred_attribute':['h1','h2','h3']}
and what I get is:
[{'attribute1':'p1_attribute1',
'attribute2':'p1_attribute2',
'referred_attribute':'h1'},
{'attribute1':'p1_attribute1',
'attribute2':'p1_attribute2',
'referred_attribute':'h2'},
{'attribute1':'p1_attribute1',
'attribute2':'p1_attribute2',
'referred_attribute':'h3'}]
Is there a way to get my desired result directly from a queryset? As I do not want to manually re-arrange the above result!
Do this instead:
p1 = Person.objects.get(attribute3="p1's attribute")
p1 is the instance of the person, you can then retrieve all your attributes from it. eg:
p1_attribute1 = p1.attribute1
p1_hobbies = p1.referred_attribute1.all()
Now you can rearrange that data however you like, for example, making a list of the hobbies as follows:
hobby_list = p1_hobbies.values_list('name', flat=True)
I would rename referred_attribute1 to hobbies for code readability
Something like this should work in this case:
[{'attribute1': res.attribute1, 'attribute2': res.attribute2,
'referred_attribute1': [a.name for a in res.referred_attribute1.all()]}
for res in Person.objects.prefetch_related('referred_attribute1').filter(attribute3="p1's attribute")]
Also see Django's Prefetch-related

order_by intermediate table for given relation in SQL with Django ORM

class Nutrient(models.Model):
tagname = models.CharField(max_length=10)
class FoodNutrientAmount(models.Model):
nutrient = models.ForeignKey(Nutrient)
food = models.ForeignKey(Food)
amount = models.FloatField()
class Food(models.Model):
nutrients = models.ManyToManyField(
Nutrient,
through=FoodNutrientAmount,
)
So, I can get the Foods ordered by the amount of tagname=FOL Nutrient with a list comprehension:
ordered_fnas = FoodNutrientAmount.objects.filter(
nutrient__tagname="FOL"
).order_by('-amount')
ordered_foods_by_most_fol = [fna.food for fna in ordered_fnas]
Can I get such an iterable as a queryset without taking the whole thing into memory?
Maybe there is a different approach using Food.objects.annotate or extra? I can't think of a great way to do it at the moment.
I can get close with values_list; but, I get the ordered list of pks and not the queryset of Food objects that I want.
FoodNutrientAmount.objects.filter(
nutrient__tagname='FOL'
).order_by('-amount').v‌​alues_list('food', flat=True)
Edit:
This is a Many-to-many relationship. So you can probably leverage that. How about adding default ordering to FoodNutrientAmount and then you can just do normal manytomany queries.
class FoodNutrientAmount(models.Model):
nutrient = models.ForeignKey(Nutrient)
food = models.ForeignKey(Food)
amount = models.FloatField()
class Meta:
ordering = ('-amount',)
Then you can just call -
nutritious_foods = Food.objects.filter(nutrients__tagname='FOL').order_by('foodnutrientamount')

Django annotate usage over an model instance other than query item

I have a query such that
em =Employer.objects.filter(id=1).annotate(overall_value = Sum('companyreview__overallRating'))
em[0].overall_value
As you see I want to sum of overallRating field of all companyreview objects whose employer has id = 1.
The query above does what I want but I am sure that there is a way to get the sum from an Employer instance.
How can I implement this query like
em =Employer.objects.get(id=1)
rate = em.companyreview_set.all().annotate(overall_value = Sum('overallRating'))
rate.overall_value
?
Thanks
Use aggregate:
e.companyreview_set.aggregate(overall_value = Sum('overall_rating'))
For:
class Employer(models.Model):
name = models.CharField(max_length=100)
class CompanyReview(models.Model):
employer = models.ForeignKey(Employer)
overall_rating = models.IntegerField()