Filtering data by date range - django

I have my payment model I what to be able to select by date
class LeasePayment(CommonInfo):
version = IntegerVersionField( )
amount = models.DecimalField(max_digits=7, decimal_places=2)
lease = models.ForeignKey(Lease)
leaseterm = models.ForeignKey(LeaseTerm)
payment_date = models.DateTimeField()
method = models.CharField(max_length=2, default='Ch',
choices=PAYMENT_METHOD_CHOICES)
Basically I want to be able to input 2 dates and display all the data between them . Righ now I started to implement this solution https://groups.google.com/forum/#!topic/django-filter/lbi_B4zYq4M based on django_filter. However since the task is pretty trivial was wondering if there an easier way.

Try to use date__range it will return data from database in selected date interval:
LeasePayment.objects.filter(payment_date__range=[start_date, end_date])

Related

query to django model to find best company sale in the month

I have two django model one "company" and the other is "MonthlyReport" of the company
I want to find out which company sale in current month had more than 20% of previous month sale
class Company(models.Model):
name = models.CharField(max_length=50)
class MonthlyReport(models.Model):
company = models.ForeignKey(Company,on_delete=models.CASCADE)
sale = models.IntegerField()
date = models.DateField()
How can i figure out this issue to find a company that has more than 20% sales over the previous month
You can certainly do it using the ORM. You will need to combine Max (or SUM depending on your use case) with a Q() expression filter and annotate the percentage increase to the queryset before filtering it.
You could do it in a single piece of code, but I have split it out because getting the dates and the query expressions are quite long. I have also put the increase value in a separate variable, rather than hardcoding it.
from datetime import datetime, timedelta
from django.db.models import Max, Q
SALES_INCREASE = 1.2
# Get the start dates of this month and last month
this_month = datetime.now().date().replace(day=1)
last_month = (this_month - timedelta(days=15)).replace(day=1)
# Get the maximum sale this month
amount_this_month = Max('monthlyreport__sale',
filter=Q(monthlyreport__date__gte=this_month))
# Get the maximum sale last month, but before this month
amount_last_month = Max('monthlyreport__sale',
filter=Q(monthlyreport__date__gte=last_month) & \
Q(monthlyreport__date__lt=this_month))
Company.objects.annotate(
percentage_increase=amount_this_month/amount_last_month
).filter(percentage_increase__gte=SALES_INCREASE)
Edit - removed incorrect code addition
There is probably a way to do this using ORM, but I would just go with python way:
First add related name to MonthlyReport
class Company(models.Model):
name = models.CharField(max_length=50)
class MonthlyReport(models.Model):
company = models.ForeignKey(Company, related_name="monthly_reports", on_delete=models.CASCADE)
sale = models.IntegerField()
date = models.DateField()
Then
best_companies = []
companies = Company.objects.all()
for company in companies:
two_last_monthly_reports = company.monthly_reports.order_by("date")[:2]
previous_report = two_last_monthly_reports[0]
current_report = two_last_monthly_reports[1]
if current_report.sale / previous_report.sale > 1.2:
best_companies.append(company)

How could you make this really reaaally complicated raw SQL query with django's ORM?

Good day, everyone. Hope you're doing well. I'm a Django newbie, trying to learn the basics of RESTful development while helping in a small app project. Currently, there's a really difficult query that I must do to create a calculated field that updates my student's status accordingly to the time interval the classes are in. First, let me explain the models:
class StudentReport(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE,)
headroom_teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE,)
upload = models.ForeignKey(Upload, on_delete=models.CASCADE, related_name='reports', blank=True, null=True,)
exams_date = models.DateTimeField(null=True, blank=True)
#Other fields that don't matter
class ExamCycle(models.Model):
student = models.ForeignKey(student, on_delete=models.CASCADE,)
headroom_teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE,)
#Other fields that don't matter
class RecommendedClasses(models.Model):
report = models.ForeignKey(Report, on_delete=models.CASCADE,)
range_start = models.DateField(null=True)
range_end = models.DateField(null=True)
# Other fields that don't matter
class StudentStatus(models.TextChoices):
enrolled = 'enrolled' #started class
anxious_for_exams = 'anxious_for_exams'
sticked_with_it = 'sticked_with_it' #already passed one cycle
So this app will help the management of a Cram school. We first do an initial report of the student and its best/worst subjects in StudentReport. Then a RecommendedClasses object is created that tells him which clases he should enroll in. Finally, we have a cycle of exams (let's say 4 times a year). After he completes each exam, another report is created and he can be recommended a new class or to move on the next level of its previous class.
I'll use the choices in StudentStatus to calculate an annotated field that I will call status on my RecommendedClasses report model. I'm having issues with the sticked_with_it status because it's a query that it's done after one cycle is completed and two reports have been made (Two because this query must be done in StudentStatus, after 2nd Report is created). A 'sticked_with_it' student has a report created after exams_date where RecommendedClasses was created and the future exams_date time value falls within the 30 days before range_start and 60 days after the range_end values of the recommendation (Don't question this, it's just the way the higherups want the status)
I have already come up with two ways to do it, but one is with a RAW SQL query and the other is waaay to complicated and slow. Here it is:
SELECT rec.id AS rec_id FROM
school_recommendedclasses rec LEFT JOIN
school_report original_report
ON rec.report_id = original_report.id
AND rec.teacher_id = original_report.teacher_id
JOIN reports_report2 future_report
ON future_report.exams_date > original_report.exams_date
AND future_report.student_id = original_report.student_id
AND future_report.`exams_date` > (rec.`range_start` - INTERVAL 30 DAY)
AND future_report.`exams_date` <
(rec.`range_end` + INTERVAL 60 DAY)
AND original_report.student_id = future_report.student_id
How can I transfer this to a proper DJANGO ORM that is not so painfully unoptimized? I'll show you the other way in the comments.
FWIW, I find this easier to read, but there's very little wrong with your query.
Transforming this to your ORM should be straightforward, and any further optimisations are down to indexes...
SELECT r.id rec_id
FROM reports_recommendation r
JOIN reports_report2 o
ON o.id = r.report_id
AND o.provider_id = r.provider_id
JOIN reports_report2 f
ON f.initial_exam_date > o.initial_exam_date
AND f.patient_id = o.patient_id
AND f.initial_exam_date > r.range_start - INTERVAL 30 DAY
AND f.initial_exam_date < r.range_end + INTERVAL 60 DAY
AND f.provider_id = o.provider_id

Get the top n rows of each day from same table in Django

I am sure this is not a novel/new problem. I really tried to look into other solutions. However, I could not find how to solve this.
I have a model like
class Deal(models.Model):
title = models.CharField(max_length=1500)
viewCounter = models.SmallIntegerField(default=0)
thumbsUpCounter = models.SmallIntegerField(default=0)
createDateTime = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=False, related_name='deal_owner',
editable=False)
Now I want to get top 10 (or less) deals ordering by thumbsUpCounter and viewCounter of every day. I tried to look into the Subquery and Outerref. However, I could not figure out how can I get the right results.
** I am using MySQL.
Thanks in advance.
try
from django.db.models.functions import TruncDate
query = Deal.objects.annotate(date=TruncDate('createDateTime'))\ # extract date
.values('date')\ # group by date
.order_by('-thumbsUpCounter')\ # order by
[:10] # slice first 10

Django auto repost data a week, month or a year depending on it's frequency of recurrence

In my django project I am trying to repost a record based on its frequency of recurrence which could be daily, weekly, monthly etc. Let's say a record is posted today and it's frequency of recurrence is weekly, I want the record to keep reappearing on a weekly basis on the 'day' and 'time' as the previous week when it was created and so on, that is, an old record will be new and top of other records which will now be older than it based on frequency of recurrence.
models.py
class Menu(models.Model):
none = 0
Daily = 1
Weekly = 7
Monthly = 30
Quarterly = 90
SemiAnual = 180
Yearly = 365
Frequency_Of_Reocurrence = (
(none, "None"),
(Daily, "Daily"),
(Weekly, "Weekly"),
(Monthly, "Monthly"),
(Quarterly, "After 3 Months"),
(SemiAnual, "After 6 Months"),
(Yearly, "After 12 Months")
)
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)
name = models.CharField(verbose_name="food name", null=False, blank=False, max_length=100)
description = models.TextField(verbose_name="Food Description", max_length=350, null=False, blank=False)
image = models.ImageField(upload_to=user_directory_path, default='veggies.jpg')
isrecurring = models.BooleanField(default=False)
frequencyofreocurrence = models.IntegerField(choices=Frequency_Of_Recurrence)
datetimecreated = models.DateTimeField(verbose_name='date-time-created', auto_now_add=True)
How exactly can I achieve what I'm trying to do in my views.
Thanks in advance.
You want to set up a cron job to check on a daily basis, have conditions to determine if a particular record is due for repost then repost that (update the time stamp if you’re not looking to create a new record). I don’t write Django so I can’t give specifics.
a little bit complication to solve it:
from django.db.models import IntegerField, F, ExpressionWrapper
from django.db.models.functions import TruncDate, Mod, Now
qset = Menu.objects.exclude(Frequency_Of_Reocurrence = 0)
.annotate(duration = ExpressionWrapper(
(TruncDate(Now())-TruncDate('datetimecreated'))/F('Frequency_Of_Reocurrence'),
output_field = IntegerField())
.annotate(reorder = ExpressionWrapper(Mod('duration', 10**8), output_field=IntegerField()))
.filter(reorder = 0).order_by('datetimecreated').values()
maybe you can refine the code.

Define time period in database for analyses

I want to store timeseries in a database. The values of these timeseries are usually defined for a certain period, for example:
country population in 2014, 2015, 2016, etc.
number of houses in country in 2014, 2015, 2016
I want to combine the data of these varabiales to be able to do some statstics, so housing vs population. This is only possible if I make sure the time periods are exactly the same. The periods are usually on a per year/quarter/month basis. How to best store these values such that I can later compare them?
I currently use start_date (datetime) and end_date (datetime), which obviously works but needs a good GUI to prevent that one person enters for example:
start = 1-1-2016 & end = 31-12=2016
while another would enter:
start = 1-1-2016 & end = 1-1=2017
I think it would be a good idea to keep the freedom of defining the period with the user but help them in defining the right thing. How would you suggest to do this?
BTW: I work with Django so my current model has the following two fields:
period_start = models.DateField(null=False)
period_end = models.DateField(null=False)
Edit 8-5-2018 10:32: added some information on storing data
Some extra information for added clarity:
I store my data in two tables: (1) the variable definition and (2) the values.
Variable defintion looks roughly like this:
class VarDef(models.Model):
name = models.CharField(max_length=2000, null=False)
unit = models.CharField(max_length=20, null=False)
desc = models.CharField(max_length=2000, blank=True, null=True)
class VarValue(models.Model):
value = models.DecimalField(max_digits=60, decimal_places=20, null=False)
var = models.ForeignKey(VarDef, on_delete=models.CASCADE, null=False,
related_name='var_values')
period_start = models.DateField(null=False)
period_end = models.DateField(null=False)
It is hard to answer since I don't have your full code(models, views etc). But keep in mind that you can query django datetime fields using lt and gt like this:
import datetime
# user input from your view, I hardcoded just for the sake of the example
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
house_data = HousesData.objects.filter(period_start__gt=start_date, period_end__lt=end_date)).all()
country_data = CountryData.objects.filter(period_start__gt=start_date, period_end__lt=end_date)).all()
# Do the rest of your calculation