How do I get something like repeat customers in Django - python

My models look something like this:
class Customer(models.Model):
name = models.CharField(max_length=100)
class Order(models.Model):
customer = models.ForeignKey(Customer)
date = models.DateField()
total = models.DecimalField(max_digits=5, decimal_places=2)
I then have a queryset of orders:
from datetime import datetime
start_date = datetime(year=2009, month=6, day=1)
end_date = datetime(year=2009, month=11, day=1)
orders = Order.objects.filter(date__lte=end_date).filter(date__gte=start_date)
Now, I want to find out which customers made multiple orders between those times, how many orders they made, and what their average total is. I get the feeling that I should be using Django 1.1's new aggregation features, but I can't really wrap my head around it.

Always base your query around the object in which you are primarily interested in:
repeat_customers = Customer.objects.annotate(order_count=Count('order'))\
.filter(order_count__gt=1)
Then if you want to annotate with their totals (you could alternatively do this in the annotation above, I'm just separating the code for readability):
repeat_customers = repeat_customers.annotate(avg_total=Avg('order__total'))

This would be a good use for Django 1.1's annotate() functionality, which is part of aggregateion. Specifically, you'll probably want to use the values() function.

Related

Custom TruncFunc in Django ORM

I have a Django model with the following structure:
class BBPerformance(models.Model):
marketcap_change = models.FloatField(verbose_name="marketcap change", null=True, blank=True)
bb_change = models.FloatField(verbose_name="bestbuy change", null=True, blank=True)
created_at = models.DateTimeField(verbose_name="created at", auto_now_add=True)
updated_at = models.DateTimeField(verbose_name="updated at", auto_now=True)
I would like to have an Avg aggregate function on objects for every 3 days.
for example I write a queryset that do this aggregation for each day or with something like TruncDay function.
queryset = BBPerformance.objects.annotate(day=TruncDay('created_at')).values('day').annotate(marketcap_avg=Avg('marketcap_change'),bb_avg=Avg('bb_change')
How can I have a queryset of the aggregated value with 3-days interval and the index of the second day of that interval?
I guess it's impossible on DB level (and Trunc is DB level function) as only month, days weeks and so on are supported in Postgres and Oracle.
So what I would suggest is to use TruncDay and then add python code to group those by 3 days.
The following should work, although it's slightly ugly.
If you get the difference in days between each row's date and the min date you can then take the Mod of this difference to work out how many days you need to shift to get "middle" date. This middle date can then be grouped on using a values query
import datetime
from django.db.models import F, IntegerField, Avg, Min, DurationField, DateField
from django.db.models.functions import Cast, Mod, Extract
BBPerformance.objects.order_by(
'created_at'
).annotate(
diff=F('created_at__date') - BBPerformance.objects.aggregate(min=Min('created_at__date'))['min']
).annotate(
diff_days=Cast(Extract('diff', 'days'), output_field=IntegerField())
).annotate(
to_shift=Mod('diff_days', 3) - 1
).annotate(
grouped_date=Cast(F('created_at__date') - Cast(F('to_shift') * datetime.timedelta(days=1), output_field=DurationField()), output_field=DateField())
).order_by(
'grouped_date'
).values(
'grouped_date'
).annotate(
marketcap_avg=Avg('marketcap_change'),
bb_avg=Avg('bb_change')
)

How to filter date According to Today Current Date in Django?

I am trying to filter date in Django according to current date, But it's displaying mr 0 results, Please let me know Where I am mistaking.
Hers is my models.py file...
class Customer(models.Model):
name = models.CharField(null=True, blannk=True)
customer_date = models.DateTimeField(null=True, blannk=True)
here is my views.py file, where i am trying to get date according to today date...
from datetime import datetime, timedelta, time, date
def getdate(request):
today=datetime.today()
customer_data = Customer.objects.filter(customer_date=today).count()
print(customer_data, "Count Values")
I see some issue in your date filter. When you do:
datetime.datetime.today()
#2020-11-04 10:57:22.214606
this give complete timestamp.
However you want to do date match only.so, try something like code.
today = datetime.today().date()
#today=datetime.today()
customer_data = Customer.objects.filter(customer_date__date=today).count()
hope this may help your query.
I saw you mistyped blank as blannk

Django: Get Months since a certain datetime

I'm trying to get the number of months since a model is created.
My Model looks like this:
class Plan(models.Model):
title = models.CharField(max_length=50)
date_created = models.DateTimeField(default=timezone.now)
plan_type = models.IntegerField()
owner = models.ForeignKey(User, on_delete=models.CASCADE)
Now i want to make a method that returns the number of months since the date_created.
Tanks for any help :D
Comparing dates creates a datetime.timedelta object that you can use to get the difference between dates.
from datetime import timedelta
from django.utils.timezone import now
delta: timedelta = now() - plan.date_created
delta.days # Number of days between dates.
You can then use that value to convert it to months or years.
The other alternative would be a bit more complicated, but since DateTimeField returns a datetime.datetime object, you can also access the month number of the original date and compare it against todays date.
e.g.
from django.utils.timezone import now
month_diff = now().month - plan.date_created.month
The problem with the second alternative is that you then have to take into account if they are the same year or not, and if they are not you then have to take that into account when you get the month difference.
You can write a property in your model like
from django.utils import timezone
class Plan(models.Model):
title = models.CharField(max_length=50)
...
#property
def get_month(self):
return self.date_created.month - timezone.now().month
Then you can get the value like this
>>> Plan.objects.first().get_month
4

Class & View for displaying Max & Avg values for each day in Django

I am extremely new to python (and django) and I've searched but honestly I'm not sure if I'm searching for the right stuff so decided to ask for some help.
Here is my class. I have a script setup to enter and save these values every 5 minutes into the db.
class Entry(models.Model):
class Meta:
ordering = ['time']
time = models.DateTimeField(default=timezone.now)
inside = models.FloatField()
outside = models.FloatField()
gavg = models.IntegerField()
ghigh = models.IntegerField()
I am wanting to setup a class/view to display the date obviously, the average(and high) for 'inside' & 'outside' for that day. The average of gavg for that day. The high for 'ghigh' that day. And this would need to be done for every single day from the Entry class.
I've tried to understand what I need to do in ORM but had issues with timezone aware datetime. I'm guessing I need another class, and I've seen some functions out there that might do what I need but I am having a difficulty time visualizing how the class should be defined and how/where the average/high functions should go. Or would this be better suited as an SQL query?
You can get the aggregated result as below,
from django.db.models import Avg, Max
aggrate_result = Entry.objects.filter(time=filter_date).aggregate(avg_inside=Avg('inside'), avg_outside=Avg('outside'),
max_inside=Max('inside'), max_outside=Max('outside'))
You should read the official documentaion of django for more details regaurding Aggregation.
In your view, you could display the aggregated response as below,
from django.http import JsonResponse
from django.db.models import Avg, Max
def my_sample_view(request):
aggrate_result = Entry.objects.filter(time=filter_date).aggregate(avg_inside=Avg('inside'), avg_outside=Avg('outside'),
max_inside=Max('inside'), max_outside=Max('outside'))
return JsonResponse(aggrate_result)
Note: You should go through the official doc to get familier with the stuffs. Happy learning ;)
Take a look at Celery, you can run a schedule which will update all the objects in database per day.
from datetime import timedelta
from django.utils import timezone
def update_previous_day_entires():
current_day = timezone.now()
previous_day = current_day - timedelta(hours=24)
entries = Entry.objects.filter(time__gt=previous_day,time__lt=current_day)
# Calculate average and high here
Entry.objects.filter(time__gt=previous_day,time__lt=current_day).update(
gavg=avg,ghigh=high)
Thanks to both of you guys. I used a combination of both to obtain what I needed.
from datetime import timedelta
from django.utils import timezone
from django.db.models import Avg, Max
from temp.models import *
import temp.views
import django
def update_avg(self, start, end):
entries_for_period = Entry.objects.filter(time__gte=start, time__lte=end)
avg_values = ['inside', 'outside', 'gavg']
average_value = entries_for_period.aggregate(*[Avg(x) for x in avg_values])
return {x:average_value['%s__avg' % x] for x in avg_values}
def update_high(self, start, end):
entries_for_period2 = Entry.objects.filter(time__gte=start, time__lte=end)
high_values = ['inside', 'outside', 'ghigh']
high_value = entries_for_period2.aggregate(*[Max(x) for x in high_values])
return {x:high_value['%s__max' % x] for x in high_values}
current_day = timezone.now()
previous_day = current_day - timedelta(hours=24)
date_entry = previous_day.date()
avg_choice = update_avg(date_entry,previous_day,current_day)
high_choice = update_high(date_entry,previous_day,current_day)
history = History(date=date_entry, avginside=round(avg_choice['inside'],2),highinside=round(high_choice['inside'],2),avgoutside=round(avg_choice['outside'],2),highoutside=high_choice['outside'],avgg=avg_choice['gavg'],highg=high_choice['ghigh'],starttime=previous_day,endtime=current_day)
history.save()
Now I can get a daily snapshot of the average and high values of my model's input. Thanks!

Django: DateTime query

I have a model
class Booked(models.Model):
start_date_time = models.DateTimeField()
end_date_time = models.DateTimeField()
resource = models.ForeignKey(Resouce)
How do I check if a particular datetime range doesn't fall between the start or end datetimes of any of the booked objects?
I want to check if I can book a resource with say book_start_date and book_end_date without it being already booked during that range
Use __lte and __gte with exists() to check if there is something in the date range:
Booked.objects.exists(start_date_time__lte=book_end_date,
end_date_time__gte=book_start_date)
See also: Determine Whether Two Date Ranges Overlap.

Categories

Resources