Django annotate usage over an model instance other than query item - django

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()

Related

Using Multiple Annotations in Django

Here is my data base:
class User(models.Model):
Name = models.TextField(null=True, blank=True, default=None)
class Salary(models.Model):
value = models.PositiveIntegerField(default=1)
name = models.ForeignKey(User, related_name='salarys', on_delete=models.CASCADE)
class Expense(models.Model):
value = models.PositiveIntegerField(default=1)
name = models.ForeignKey(User, related_name='expenses', on_delete=models.CASCADE)
I want to add all the salary and expenses of a user.
queryset = User.objects.annotate(total_salary=Sum('salarys__value', distinct=True) ,total_expense=Sum('expenses__value', distinct=True))
Here is my data:
User table
id=1; name= ram
Salary table
id =1; value = 12000
id = 2; value = 8000
Expense table
id =1; value=5000
id=2; value = 3000
expected output : total_Salary = 20000; total_expense=8000 output
obtained : total_salary= 40000; total_expense = 16000
Every output is multiplied the number of times the data in another table. Can anyone help me through this
looks like it is not possible if you are trying to combine like aggregates. Only works for Count with distinct. For closure, please refer to docs: https://docs.djangoproject.com/en/4.0/topics/db/aggregation/#combining-multiple-aggregations
Since annotate returns a queryset instead of a dict(in case of aggregate), you can try chaining multiple like values().annotate("field1").annotate("field2"). Worked for me, though please check sql from your queryset to debug in case you cannot run your query as desired.
Can you try this ?
queryset = User.objects.all().annotate(total_salary=Sum('salarys__value') ,total_expense=Sum('expenses__value'))

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')

Trying to filter based upon a value in another table

I have 2 tables as
class ItemFollowers(models.Model):
item = models.ForeignKey(Items, models.DO_NOTHING, db_column='item')
user = models.ForeignKey(AuthUser, models.DO_NOTHING, db_column='user')
And the other one is
class UsrPosts(models.Model):
item = models.ForeignKey('Items', models.DO_NOTHING, db_column='item')
# Some other fields
How can I select the UsrPosts related to the items followed by some user? i.e. I can have records in ItemFollowers like (item0, user0), (item1, user0), (item5, user0). I need to filter UsrPosts based upon the user (aka. request.user.id)
Here is a inefficient non-working way to get UsrPostts
itms = ItemFollowers.objects.filter(user_id=request.user.id)
qry = Q(item_id=itms[0].item.id) | ..... | Q(item_id=itms[N].item.id)
posts = UsrPosts.objects.filter(qry)
Is there some filter magic to get it in one transaction?
itms = ItemFollowers.objects.filter(user_id=request.user.id).values‌​_list('item')
posts = UsrPosts.objects.filter(item__in = itms)

Access foreign key objects in 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

Django: Annotate based on an annotation

Let's say I'm using Django to manage a database about athletes:
class Player(models.Model):
name = models.CharField()
weight = models.DecimalField()
team = models.ForeignKey('Team')
class Team(models.Model):
name = models.CharField()
sport = models.ForeignKey('Sport')
class Sport(models.Model):
name = models.CharField()
Let's say I wanted to compute the average weight of the players on each team. I think I'd do:
Team.objects.annotate(avg_weight=Avg(player__weight))
But now say that I want to compute the variance of team weights within each sport. Is there a way to do that using the Django ORM? How about using the extra() method on a QuerySet? Any advice is much appreciated.
you can use query like this :
class SumSubquery(Subquery):
template = "(SELECT SUM(`%(field)s`) From (%(subquery)s _sum))"
output_field = models.Floatfield()
def as_sql(self, compiler, connection, template=None, **extra_context):
connection.ops.check_expression_support(self)
template_params = {**self.extra, **extra_context}
template_params['subquery'], sql_params = self.queryset.query.get_compiler(connection=connection).as_sql()
template_params["field"] = list(self.queryset.query.annontation_select_mask)[0]
sql = template % template_params
return sql, sql_params
Team.objects.all().values("sport__name").annotate(variance=SumSubquery(Player.objects.filter(team__sport_id=OuterRef("sport_id")).annotate(sum_pow=ExpressionWrapper((Avg("team__players__weight") - F("weight"))**2,output_field=models.Floatfield())).values("sum_pow"))/(Count("players", output_field=models.FloatField())-1))
and add related name to model like this:
class Player(models.Model):
name = models.CharField()
weight = models.DecimalField()
team = models.ForeignKey('Team', related_name="players")
I'm going to assume (perhaps incorrectly) that you mean by 'variance' the difference between maximum and minimum weights. If so, you can generate more than one aggregate with a single query, like so:
from django.db.models import Avg, Max, Min
Team.objects.aggregate(Avg('player__weight'), Max('player__weight'), Min('player__weight'))
This is taken from the django docs on generating aggregation over a queryset.