In the following queryset I am filtering planned hours per week (displayval is my week in this queryset) by employee. I would like to add an item for planned hours = 0 when the employee has no hours planned for a week I'm filtering by.
What's the easiest way to achieve this?
def DesignHubR(request):
emp3_list = Projectsummaryplannedhours.objects.values_list('displayval', 'employeename')
.filter(businessunit='a')
.filter(billinggroup__startswith='PLS - Project')
.filter(Q(displayval=sunday2)|Q(displayval=sunday))
.annotate(plannedhours__sum=Sum('plannedhours'))
emp3 = map(lambda x: {'date': x[0], 'employee_name': x[1], 'planned_hours': x[2]}, emp3_list)
context = {'sunday': sunday, 'sunday2': sunday2, 'emp3': emp3}
return render(request,'department_hub_ple.html', context)
I think that you can use the Coalesce(*expressions, **extra) function to solve your problem.
Accepts a list of at least two field names or expressions and returns the first non-null value (note that an empty string is not considered a null value).
So your query will be looking like:
from django.db.models import Sum, Value
from django.db.models.functions import Coalesce
emp3_list = \
Projectsummaryplannedhours.objects.\
filter(
Q(businessunit='a') &
Q(billinggroup__startswith='PLS - Project') &
(Q(displayval=sunday2) | Q(displayval=sunday))
).\
annotate(plannedhours__sum=Coalesce(
Sum('plannedhours'), Value(0)
)
).\
values_list('displayval', 'employeename')
See https://docs.djangoproject.com/en/1.9/ref/models/database-functions/#coalesce for more information.
This will help you to get plannedhours__sum = 0 if no entries to sum exists. If you also want to add additional parameter to each entry where plannedhours__sum = 0 you can use Django conditional expression.Read about Case expression for more information (https://docs.djangoproject.com/en/1.11/ref/models/conditional-expressions/#case).
Case() accepts any number of When() objects as individual arguments. Other options are provided using keyword arguments. If none of the conditions evaluate to TRUE, then the expression given with the default keyword argument is returned. If a default argument isn’t provided, None is used.
from django.db.models import Sum, Value, IntegerField
from django.db.models.functions import Coalesce
emp3_list = \
Projectsummaryplannedhours.objects.\
filter(
Q(businessunit='a') &
Q(billinggroup__startswith='PLS - Project') &
(Q(displayval=sunday2) | Q(displayval=sunday))
).\
annotate(plannedhours__sum=Coalesce(
Sum('plannedhours'), Value(0)
),
x=Case(When(plannedhours__sum=0, then=Value(0)),
output_field=IntegerField())
).\
values_list('displayval', 'employeename')
This will give you additional parameter x equals 0 if planned hours = 0 and None elsewhere. You can also filter emp3_list by annotated values.
As a result you can pass your queryset to a template context = {'sunday': sunday, 'sunday2': sunday2, 'emp3': emp3_list}, iterate over it there and get the attributes you need:
for q in emp3_list:
print(q[0], q[1], q[2])
Hope it will help you.
Related
order_list_cases =(order_list.values('user_id').annotate(dcount=Count('user_id'),customer_paid_sum=(Sum('invoice__transaction_invoice__transaction_amount'))
In this code, the customer_paid_sumis returning None, I want to give it as 0 if it is None. I tried else case along with it but didn't work.
You can work with Coalesce [Django-doc] to return 0 instead of None if the aggregate works over an empty collection:
from django.db.models import Value
from django.db.models.functions import Coalesce
order_list_cases = order_list.values('user_id').annotate(
dcount=Count('user_id'),
customer_paid_sum=Coalesce(
Sum('invoice__transaction_invoice__transaction_amount'),
Value(0)
)
)
I have the following models:
class Materiale(models.Model):
sottocategoria = models.ForeignKey(Sottocategoria, on_delete=models.CASCADE, null=True)
quantita=models.DecimalField(')
prezzo=models.DecimalField()
data=models.DateField(default="GG/MM/YYYY")
I wanna calculate the value given by the following expressions PREZZO*QUANTIA in a monthly's view (in other words the total sum of PRZZO*QUANTITA of all items in a single month), but my code does not work:
Monthly_views=Materiale.objects.filter(data__year='2020').values_list('month').annotate(totale_mensile=F(('quantita')*F('prezzo')))
Use values() method instead of values_list()
from django.db.models import F, Sum
result = Materiale.objects.annotate(totale_mensile=F('quantita') * F('prezzo')
).values('data__month').annotate(totale_mensile_sum=Sum('totale_mensile')))
or simply
result = Materiale.objects.values('data__month').annotate(totale_mensile_sum=Sum(F('quantita') * F('prezzo')))
Try filtering by month also
Monthly_views=Materiale.objects.filter(data__year='2020').filter(data_month='4')
I have to filter data from model based on the run time values. I am getting 5 values via query string. My querystring is like below:
http://127.0.0.1:8000/personal/search/?month=&year=&account=&deliveryManagedFrom=&marketmName=
So, I want to include all or none of the values in the filter so that it displays the desired result. Below is the filter query which I am writing:
sum_tt_count = NetworkRelatedInformation.objects.filter(month=month, year=year, account_id=account, account__deliveryManagedFrom=deliveryManagedFrom, account__marketName=market).aggregate(Sum('tt_count'))
totalttcount = sum_tt_count['tt_count__sum']
It is working well in case, all the values have been provided.
In case, if any value is blank, it should not consider that value and display output as per other filter criteria.
Pls suggest how to implement an OR filter with 5 data inputs. It is not necessary that all 5 data inputs have values . So the value can be None or the value in the querystring
Filter the request for non-empty values and then use dictionary expansion to do the query.
q = {k:v for k, v in request.GET.items() if v}
sum_tt_count = NetworkRelatedInformation.objects.filter(**q).aggregate(Sum('tt_count'))
You can do it using Q object
from django.db.models import Q
NetworkRelatedInformation.objects.filter(Q(month__isnull=True) | Q(month=month), Q(year__isnull=True) | Q(year=year)).aggregate(Sum('tt_count'))
For handling the None values i have to explicitly write the below code.
account = request.GET.get('account')
if account is '':
account = None
month = request.GET.get('month')
if month is '':
month = None
year = request.GET.get('year')
if year is '':
year = None
sum_alarm_count = NetworkRelatedInformation.objects.filter(Q(month=month) | Q(year=year) | Q(account_id=account)) \
.aggregate(Sum('alarm_count'))
totalalarmcount = sum_alarm_count['alarm_count__sum']
I've managed to get a query working that sums up all the child rows of the parent class:
subq = db.session.query(Transaction.budget_id, func.sum(Transaction.amount).label("total"), func.count('*').label("count"))
.group_by(Transaction.budget_id).subquery()
return db.session.query(Budget.ID, Budget.name, Budget.start_date, Budget.end_date, Budget.amount, subq.c.total, subq.c.count)
.outerjoin(subq, Budget.ID==subq.c.budget_id)
.order_by(Budget.start_date.desc()).limit(count)
The problem is that it doesn't work when Budget doesn't have any Transaction subclasses. It returns None, which throws a spanner in the works. I want the sum() and count() functions to return 0 instead.
from sqlalchemy.sql.functions import coalesce
...
return db.session.query(Budget.ID,
Budget.name,
Budget.start_date,
Budget.end_date,
Budget.amount,
coalesce(subq.c.total, 0),
coalesce(subq.c.count, 0))
.outerjoin(subq, Budget.ID==subq.c.budget_id)
.order_by(Budget.start_date.desc()).limit(count)
2022 Answer
I want the sum() and count() functions to return 0 instead.
Based on the answer of #antonio_antuan but with sqlalchemy-1.4, now you can:
from sqlalchemy import func, select
stmt = select(func.coalesce(func.sum(Transaction.amount), 0))
s = db.session.execute(stmt).scalars().one()
and likewise for func.count.
I have two lists, each is made up of objects having a date. I am trying to combine them and then order by date:
combined = invoices + payments
combined.sort(key = lambda x: x.date)
All well and good. However, if there is both an invoice object and payment object on the same day, I want the payment to be placed in the list before the invoice.
Just do this instead:
combined = payments + invoices
python iterable.sort method is guaranteed to be stable. (See python docs on standar types, 5.6.4 note 9)
That means if there are 2 elements a and b on your list such that key(a) == key(b), then they'll keep their relative order (that means, if a was placed before b on the unsorted list, it'll still be like that after it's sorted).
You should be able to do something like this to get the sorting you want:
combined.sort(key = lambda x: (x.date, 1 if x in invoices else 0))
The idea being that, as long as the objects are distinct, you can create a sorting tuple that includes an indicator of which list the object came from. That'll make it sort by the dates first, then fall over to the 2nd field if the dates match.
In addition to key=, you can also use cmp= in the sort function.
class Invoice(object):
P = 1
def __init__(self, date):
self.date = date
class Payment(object):
P = 0
def __init__(self, date):
self.date = date
l = [Invoice(10), Payment(10), Invoice(10)]
def xcmp(x, y):
c0 = cmp(x.date, y.date)
return c0 if c0 != 0 else cmp(x.__class__.P, y.__class__.P)
l.sort(cmp=xcmp)