Django, sum of fields in intermediary model with given queryset - django

I have 2 models, one of them has many to many relation with itself through other table like this.
class a(models.Model):
# fields
class b(models.Model):
from_a = models.ForeignKey(a)
to_a = models.ForeignKey(a)
count = models.PositiveIntegerField()
Now, what I wonder is, what is the best way of calculating sum of counts in b's where from_a is "something". This one seems trivial, but I can't figure it out.

from django.db.models import Sum
b.objects.filter(from_a__whatever='something').aggregate(Sum('count'))

Related

Order queryset using the number of related objects in Django

Let's say I have the following in models.py:
class A(models.Model):
...
class B(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE)
What I want to do is sort a queryset for A based on how many B objects they have, in descending order. What is the best approach to this issue? Thanks for any help.
You can work with a .annotate(…) [Django-doc] and then .order_by(…) [Django-doc]:
from django.db.models import Count
A.objects.annotate(
nb=Count('b')
).order_by('-nb')
Since django-3.2 you can work with .alias(…) [Django-doc] to prevent calculating this both as column and in the ORDER BY clause:
from django.db.models import A
A.objects.alias(
nb=Count('b')
).order_by('-nb')
This is:
queryset = A.objects.filter().order_by('B_A')
Here 'B_A' you have to put the related name
class A(models.Model):
...
class B(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE, related_name = 'B_A')

Ordering objects by calculating the sum of fields on a foreign key Django

Say I had some models defined as such:
Class Owner(models.Model):
pass
Class Item(models.Model):
value = models.IntegerField()
owners = models.ForeignKey(Owner, related_name="items")
How would I go about sorting Owners based off of the sum of the value of their items, ideally as a QuerySet that I could pass onto a template like Owner.objects.order_by('-valueofItems')
Use ORM aggregation together with order_by
from django.db.models import Sum
Owner.objects.annotate(total_value=Sum('item_value')).order_by('-total_value')

Aggregation in Django

I have the following Model in Django:
class A(models.Model):
nr = models.IntegerField()
class B(models.Model):
points = models.IntegerField()
class C(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE)
b = models.ForeignKey(B, on_delete=models.CASCADE)
So for every A there are many entries in C, and for every B there are also many entries in C. But for every entry in C there is exactly one entry in A and one in B.
I would like to sum up the B.points for a given A, but I have to go over C.
How can I do that in Django? I would know how to do that in SQL if that helps?
You can .annotate(..) [Django-doc] the As, like:
from django.db.models import Sum
A.objects.annotate(
total_points=Sum('c__b__points')
)
If you for example always want to annotate your A objects, you can define a manager for that:
class WithPointsManager(models.Manager):
def get_queryset(self):
return super().get_queryset().annotate(
total_points=Sum('c__b__points')
)
and then define this manager on the A class, like:
class A(models.Model):
nr = models.IntegerField()
objects = WithPointsManager()
So now if you perform an A.objects.all(), the As will have a total_points attribute. Note that of course this will have a certain cost at the database side.
Then all A objects that arise from this QuerySet, will contain an extra .total_points attribute that contains the sum of all Bs that are related to a C that is related to that A.
Or for a given A object, you can .aggregate(..) [Django-doc], like:
from django.db.models import Sum
some_a.c_set.aggregate(
total_points=Sum('b__points')
)['total_points']
This will return the sum of the points of related B objects of the set of Cs that are related to some_a.

How to filter based on compared values of fields in a model in django?

How to filter queryset based on comparing internal values of fields in any model?
For instance, have a look at the example below:
class Model1(models.Model):
num1 = models.IntegerField()
num2 = models.IntegerField()
Say I have to filter for the Model1 values for which num2 - num1 is less than 10.
So, how will we write the filter condition?
That is Model1.objects.filter(???)?
Also, say the fields are datetime, as below:
class Model2(models.Model):
t1 = models.DateTimeField(auto_now_add=True)
t2 = models.DateTimeField()
How can we implement a similar case as above. Say we want to filter based upon t2-t1 less than 10 hours?
You can annotate the calculation and then filter on the result.
from django.db.models import F
Model1.objects.annotate(diff=F('num1')-F('num2')).filter(diff__lt=10)

Ordering by number of one-to-many entries

I have models like this:
class Forum(models.Model):
title = models.CharField(max_length=100)
class Entry(models.Model):
entry = models.TextField()
good = models.BooleanField()
f = models.ForeignKey(Forum)
I want to list all Forum objects ordered by [the number of Entry objects in that forum satisfying good == True]. How can I do this with Django model methods?
Try this
from django.db.models import Sum
result = Forum.objects.filter(entry__good=True).annotate(Sum('entry')).order_by('-entry__sum')
-entry__sum - it's order from big to small count of good entry, if need order from small to big, then order_by('entry__sum')