Django get the sum of all columns for a particular user - python

I have a django model as follows:
class Order(models.Model):
cash=models.DecimalField(max_digits=11,decimal_places=2,default=0)
balance=models.DecimalField(max_digits=11,decimal_places=2,default=0)
current_ac=models.DecimalField(max_digits=11,decimal_places=2,default=0)
added_by = models.ForeignKey(User)
There can be multiple Orders and multiple users can create orders.
How can I get the sum of all orders for each columns for a particular user, something like
ord=Order.objects.filter(added_by.id=1).sum()
an SQL equivalent would be something like
Select sum(cash), sum (balance), sum(current_ac) from Orders where added_by = 1

You can aggregate, for example the sum of the current_ac with:
from decimal import Decimal
from django.db.models import Sum
ord=Order.objects.filter(added_by_id=1).aggregate(
total=Sum('current_ac')
)['total'] or Decimal()
or if you want to sum up the items for cash, balance and current_ac, you can work with:
from decimal import Decimal
from django.db.models import Sum
ord=Order.objects.filter(added_by_id=1).aggregate(
total_cash=Sum('current_ac'),
total_balance=Sum('balance'),
total_ac=Sum('current_ac')
)
here ord will be a dictionary that contains the corresponding values, for example:
{
'total_cash': Decimal('14.25'),
'total_balance': Decimal('13.02'),
'total_ac': Decimal('17.89')
}
or if you want to count the number of Orders, then we can work with:
from decimal import Decimal
ord=Order.objects.filter(added_by_id=1).count()
If you want to do that per User, it is more efficient to work with .annotate(…) [Django-doc].

If I get you correctly. You want to count the number of records, right? if that's the case. You can use filter and count. like in the example below:
numberOfRecords = Orders.filter(added_by=user_id).count

you can try this
from django.db.models import Sum
total=Order.objects.filter(added_by_id=1).aggregate(
total=Sum('current_ac')
)['total'] or 0

Related

Annotate query for calculate sum of 2 table value with Django ORM

I have 2 tables
Class Billing(models.Model):
id=models.AutoField(primary_key=True)
.....
#Some more fields
....
Class BillInfo(models.Model):
id=models.AutoField(primary_key=True)
billing=models.ForeignKey(Billing)
testId=models.ForeignKey(AllTests)
costOfTest=models.IntegerField(default=0)
concession=models.IntegerField(default=0)
Here BillInfo is verticle table i.e one Billing has multiple BillInfo. Here I want to calculate the Sum(costOfTest - concession) for a single Billing.
Can I achieve this using single query?
Need help, Thanks in advance.
You can write this as:
from django.db.models import F, Sum
Billing.objects.annotate(
the_sum=Sum(F('billinfo__costOfTest') - F('billinfo__concession'))
)
Here every Billing object in this QuerySet will have an extra attribute .the_sum which is the sum of all costOfTests minus the concession of all related BillingInfo objects.
The SQL query that calculates this will look, approximately as:
SELECT billing.*
SUM(billinginfo.costOfTest - billinginfo.concession) AS the_sum
FROM billing
LEFT OUTER JOIN billinginfo ON billinginfo.billing_id = billing.id
GROUP BY billing.id
So when you "materialize" the query, the query will obtain the sum for all the Billing objects in a single call.
For Billing objects without any related BillingInfo, the the_sum attribute will be None, we can avoid that by using the Coalesce [Django-doc] function:
from django.db.models import F, Sum, Value
from django.db.models.functions import Coalesce
Billing.objects.annotate(
the_sum=Coalesce(
Sum(F('billinfo__costOfTest') - F('billinfo__concession')),
Value(0)
)
)

Django: Filtering a queryset then count

I'm trying to limit the number of queries I perform on a page. The queryset returns the objects created within the last 24 hours. I then want to filter that queryset to count the objects based upon a field.
Example:
cars = self.get_queryset()
volvos_count = cars.filter(brand="Volvo").count()
mercs_count = cars.filter(brand="Merc").count()
With an increasing number of brands (in this example), the number of queries grows linearly with the number of brands that must be queried.
How can you make a single query for the cars that returns a dict of all of the unique values for brand and the number of instances within the queryset?
Result:
{'volvos': 4, 'mercs': 50, ...}
Thanks!
EDIT:
Of the comments so far, they have been close but not quite on the mark. Using a values_list('brand', flat=True) will return the brands. From there you can use
from collections import Counter
To return the totals. It would be great if there is a way to do this from a single query, but maybe it isn't possible.
To generate a count for each distinct brand, you use values in conjunction with annotate.
totals = cars.values('brand').annotate(Count('brand'))
This gives you a queryset, each of whose elements is a dictionary with brand and brand__count. You can convert that directly into a dict with the format you want:
{item['brand']: item['brand__count'] for item in totals}
SELECT brand, COUNT(*) as total
FROM cars
GROUP BY brand
ORDER BY total DESC
Equivalent:
cars.objects.all().values('brand').annotate(total=Count('brand')).order_by('total')

Django aggregate Count only True values

I'm using aggregate to get the count of a column of booleans. I want the number of True values.
DJANGO CODE:
count = Model.objects.filter(id=pk).aggregate(bool_col=Count('my_bool_col')
This returns the count of all rows.
SQL QUERY SHOULD BE:
SELECT count(CASE WHEN my_bool_col THEN 1 ELSE null END) FROM <table_name>
Here is my actual code:
stats = Team.objects.filter(id=team.id).aggregate(
goals=Sum('statistics__goals'),
assists=Sum('statistics__assists'),
min_penalty=Sum('statistics__minutes_of_penalty'),
balance=Sum('statistics__balance'),
gwg=Count('statistics__gwg'),
gk_goals_avg=Sum('statistics__gk_goals_avg'),
gk_shutout=Count('statistics__gk_shutout'),
points=Sum('statistics__points'),
)
Thanks to Peter DeGlopper suggestion to use django-aggregate-if
Here is the solution:
from django.db.models import Sum
from django.db.models import Q
from aggregate_if import Count
stats = Team.objects.filter(id=team.id).aggregate(
goals=Sum('statistics__goals'),
assists=Sum('statistics__assists'),
balance=Sum('statistics__balance'),
min_penalty=Sum('statistics__minutes_of_penalty'),
gwg=Count('statistics__gwg', only=Q(statistics__gwg=True)),
gk_goals_avg=Sum('statistics__gk_goals_avg'),
gk_shutout=Count('statistics__gk_shutout', only=Q(statistics__gk_shutout=True)),
points=Sum('statistics__points'),
)
Updated for Django 1.10. You can perform conditional aggregation now:
from django.db.models import Count, Case, When
query_set.aggregate(bool_col=Count(Case(When(my_bool_col=True, then=1))))
More information at:
https://docs.djangoproject.com/en/1.11/ref/models/conditional-expressions/#case
Update:
Since Django 1.10 you can:
from django.db.models import Count, Case, When
query_set.aggregate(
bool_col=Count(
Case(When(my_bool_col=True, then=Value(1)))
)
)
Read about the Conditional Expression classes
Old answer.
It seems what you want to do is some kind of "Conditional aggregation". Right now Aggregation functions do not support lookups like filter or exclude: fieldname__lt, fieldname__gt, ...
So you can try this:
django-aggregate-if
Description taken from the official page.
Conditional aggregates for Django queries, just like the famous SumIf and CountIf in Excel.
You can also first annotate the desired value for each team, I mean count for each team the ammount of True in the field you are interested. And then do all the aggregation you want to do.
Another Solution for count Bool is:
from django.db.models import Sum, IntegerField
from django.db.models.functions import Cast
Model.objects.filter(id=pk).annotate(bool_col=Sum(Cast('my_bool_col', IntegerField())))
Just convert False to 0 and True to 1, and then just Sum

Django Aggregation for Goals

I'm saving every Sale in a Store. I want to use aggregation to sum all of the sales in a month for every store. And i want to filter the stores that reach the goal (100.000$).
I've already came up with a solution using python and a list. But i wanted to know if there is a better solution using only the ORM.
Sales model
Store Sale Date
Store A 5.000 11/01/2014
Store A 3.000 11/01/2014
Store B 1.000 15/01/2014
Store C 8.000 17/01/2014
...
The result should be this:
Month: January
Store Amount
A 120.000
B 111.000
C 150.000
and discard
D 70.000
Thanks for your help.
Other suggested methods discard a lot of data that takes a fraction of a second to load, and that could be useful later on in your code. Hence this answer.
Instead of querying on the Sales object, you can query on the Store object. The query is roughly the same, except for the relations:
from django.db.models import Sum
stores = Store.objects.filter(sales__date__month=month, sales__date__year=year) \
.annotate(montly_sales=Sum('sales__amount')) \
.filter(montly_sales__gte=100000) \
# optionally prefetch all `sales` objects if you know you need them
.prefetch_related('sales')
>>> [s for s in stores]
[
<Store object 1>,
<Store object 2>,
etc.
]
All Store objects have an extra attribute montly_sales that has the total amount of sales for that particular month. By filtering on month and year before annotating, the annotation only uses the filtered related objects. Note that the sales attribute on the store still contains all sales for that store.
With this method, all store attributes are easily accessible, unlike when you use .values to group your results.
Without a good look at your models the best I can do is pseudocode. But I would expect you need something along the lines of
from django.db.models import Sum
results = Sales.objects.filter(date__month=month, date__year=year)
results = results.values('store')
results = results.annotate(total_sales=Sum(sale))
return results.filter(total_sales__gt=100)
Basically, what we're doing is using django's aggregation capabilities to compute the Sum of sales for each store. Per django's documentation, we can use the values function to group our results by distinct values in a given field.
In line 2 we filter our sales to only sales from this month.
In line 3, we limit our results to the values for field store.
In line 4, we annotate each result with the Sum of all sales from the
original query.
In line 5, we filter on that annotation, limiting the returned results to stores with total_sales greater than 100.
You can use annotate to handle this. Since I do not know your model structure, That is an average guess
from djnago.db.models import Sum
Sales.objects.filter(date__month=3, date__year=2014).values('store').annotate(monthly_sale=Sum('sale'))
That will return you a Queryset of Stores and their monthly sales like:
>> [
{"store": 1, "monthly_sale": 120.000},
{"store": 2, "monthly_sale": 100.000},
...
]
In above query assume you have:
Sales model have a Date or Datetime field named date
Your Sale model have a ForeignKey relation to Store
Your Sales model have a numeric field (Integer, Decimal etc.) named sale
In your resulting QuerySet, store is the id of your store record. But since it is a ForeigKey, you can use relation to get its name etc...
Sales.objects.filter(date__month=3, date__year=2014).values('store__name').annotate(monthly_sale=Sum('sale'))
>> [
{"store__name": "Store A", "monthly_sale": 120.000},
{"store__name": "Store B", "monthly_sale": 100.000},
...
]

Django ORM query GROUP BY multiple columns combined by MAX

I am using Django with MySQL. I have a model similar to the following:
class MM(models.Model):
a = models.IntegerField()
b = models.IntegerField()
c = models.DateTimeField(auto_now_add=True)
I have multiple rows that a is equal to b, and I want to perform the following SQL query:
SELECT a, b, MAX(c) AS max FROM MM GROUP BY b, a;
How can this be done with Django ORM? I have tried different approaches using annotations, but now luck so far.
Thanks a lot!
I think you can do something like:
MM.objects.all().values('b', 'a').annotate(max=Max('c'))
Note that you need to import something to use Max: from django.db.models import Max
values('b', 'a') will give GROUP BY b, a and annotate(...) will compute the MAX in your query.
You can try this also
from django.db.models import Max
mm_list=MM.objects.all().values('b','a').annotate(max=Max('c'))
for mm in mm_list:
a=mm['a']
b=mm['b']
max=mm['max']
Sum a field group by two fields.
from django.db.models import Sum
SQL
select caja_tipo_id, tipo_movimiento, sum(monto) from config_caja group by caja_tipo_id, tipo_movimiento
Django
objs = Caja.objects.values('caja_tipo__nombre','tipo_movimiento').order_by().annotate(total=Sum('monto'))

Categories