Reputation: 1203
This is model:
class Purchase(models.Model):
date = models.DateField(default=datetime.date.today,blank=False, null=True)
total_purchase = models.DecimalField(max_digits=10,decimal_places=2,blank=True, null=True)
I want to perform a month wise calculation of "total_purchase" within a specific daterange in such a way that if there is no purchase in a month the total purchase should be the previous month purchase value And if there is purchase in two months then total purchase will the addition of those two...
Example:
Suppose the date range given by user is from month of April to November.
If there is a Purchase of $2800 in month of April and $5000 in month of August and $6000 in month of October.
Then the output will be like this:
April 2800
May 2800
June 2800
July 2800
August 7800 #(2800 + 5000)
September 7800
October 13800 #(7800 + 6000)
November 13800
Any idea how to perform this in django queries?
Thank you
According to the answer given by Mr.Raydel Miranda. I have done the following
import calendar
import collections
import dateutil
start_date = datetime.date(2018, 4, 1)
end_date = datetime.date(2019, 3, 31)
results = collections.OrderedDict()
result = Purchase.objects.filter(date__gte=start_date, date__lt=end_date).annotate(real_total = Case(When(Total_Purchase__isnull=True, then=0),default=F('tal_Purchase')))
date_cursor = start_date
while date_cursor < end_date:
month_partial_total = result.filter(date__month=date_cursor.month).agggate(partial_total=Sum('real_total'))['partial_total']
results[date_cursor.month] = month_partial_total
if month_partial_total == None:
month_partial_total = int(0)
else:
month_partial_total = month_partial_total
date_cursor += dateutil.relativedelta.relativedelta(months=1)
return results
But now the output is coming like this(from the example above):
April 2800
May 0
June 0
July 0
August 5000
September 0
October 6000
November 0
Do anyone have any idea how to add between the months... I want to do something like
e = month_partial_total + month_partial_total.next
I want to add the next iteration value of every month_partial_total. I think this will solve my problem..
Any idea anyone how to perform this in django?
Thank you
Upvotes: 4
Views: 398
Reputation: 1203
Solution
According to the answer given by Mr.Raydel Miranda, Finally I got the solution to my problem...
I have done the following in my views and it worked very nicely:
import datetime
import calendar
import collections
import dateutil
start_date = datetime.date(2018, 4, 1)
end_date = datetime.date(2019, 3, 31)
results = collections.OrderedDict()
result = Purchase.objects.filter(date__gte=start_date, date__lt=end_date).annotate(real_total = Case(When(Total_Purchase__isnull=True, then=0),default=F('Total_Purchase')))
date_cursor = start_date
z = 0
while date_cursor < end_date:
month_partial_total = result.filter(date__month=date_cursor.month).aggregate(partial_total=Sum('real_total'))['partial_total']
# results[date_cursor.month] = month_partial_total
if month_partial_total == None:
month_partial_total = int(0)
e = month_partial_total
else:
e = month_partial_total
z = z + e
results[date_cursor.month] = z
date_cursor += dateutil.relativedelta.relativedelta(months=1)
return results
Thank you everyone.
Upvotes: 0
Reputation: 14360
I've noted two things in your question:
blank
or null
.Based on those things I'll propose this approach:
You could get the total for a given month, you just need to handle the case where the total_pushase
is null (as a side note, it does not make any sense to have an instance of Purchase
where total_purchase
is null, at least it must be 0).
Read about Django Conditional expressions to learn more about When
and Case
.
# Annotate the filtered objects with the correct value (null) is equivalent
# to 0 for this requirement.
result = Purchase.objects.filter(date__gte=start_date, date__lt=end_date).annotate(
real_total = Case(
When(total_purchase__isnull=True, then=0),
default=F('total_purchase')
)
)
# Then if you want to know the total for a specific month, use Sum.
month_partial_total = result.filter(
date__month=selected_month
).aggregate(
partial_total=Sum('real_total')
)['partial_total']
You could use this in a function to achieve you wanted result:
import calendar
import collections
import dateutil
def totals(start_date, end_date):
"""
start_date and end_date are datetime.date objects.
"""
results = collections.OrderedDict() # Remember order things are added.
result = Purchase.objects.filter(date__gte=start_date, date__lt=end_date).annotate(
real_total = Case(
When(total_purchase__isnull=True, then=0),
default=F('total_purchase')
)
)
date_cursor = start_date
month_partial_total = 0
while date_cursor < end_date:
# The while statement implicitly orders results (it goes from start to end).
month_partial_total += result.filter(date__month=date_cursor.month).aggregate(
partial_total=Sum('real_total')
)['partial_total']
results[date_cursor.month] = month_partial_total
# Uncomment following line if you want result contains the month names
# instead the month's integer values.
# result[calendar.month_name[month_number]] = month_partial_total
date_cursor += dateutil.relativedelta.relativedelta(months=1)
return results
Since Django 1.11 might be able to solve this problem SubQueries, but I've never used it for subquery on the same model.
Upvotes: 7