Django ORM query on fk set - django

I have a problem with lookup that looks for a value in related set.
class Room(models.Model):
name = models.Charfield(max_lentgh=64)
class Availability(models.Model):
date = models.DateField()
closed = models.BooleanField(default=False)
room = models.ForeignKey(Room)
Considering that there is one availability for every date in a year. How can use ORM query to find whether room withing given daterange (i.e. 7 days) has:
availability exist for every day within given daterange
none of the availabilities has closed=True
I was unable to find any orm examples that check whether all objects within daterange exist

You can enumerate over the dates, and ensure that it has for that date an Availability with closed=False:
from datetime import date, timedelta
rooms = Room.objects.all()
start_date = date(2022, 7, 21) # first day
for dd in range(7): # number of days
dt = start_date + timedelta(days=dd)
rooms = rooms.filter(availability__date=dt, availability__closed=False)
The rooms will after the for loop have a QuerySet with all Rooms that have for all dates in that range Availability objects with closed=False.

Related

Grouping entries from PostgreSQL database by their week or month based on timestamp value?

I have been trying to resolve a solution to this for a while now but due to my inexperience with both postgres(and SQL in general) and Django ORM I haven't been able to get a result that I can use.
I have this model:
class EventItem(models.Model):
Id = models.CharField(max_length=200, primary_key=True)
StartTime = models.DateTimeField(null=True)
EndTime = models.DateTimeField(null=True)
Duration = models.TimeField(null=True)
Creator = models.CharField(max_length=50, null=True)
and I want to get the EventItems and group the entries by their week and/or month. So for every week starting from the earliest entry group all of the events for that week into one group.
Right now I am able to return the week number for each individual item by doing this:
weeks = EventItem.objects.annotate(week=ExtractWeek('StartTime')).values('week')
But this obviously doesn't group the results and I also need to keep the columns from the original table.
You don't want to use ExtractWeek because that just gets an integer value of the week in the year, e.g. 44, this can cause clashes across datasets that exceed a year.
What you will want to use is TruncWeek which essentially caps a data value at the Monday of that week. You can combine this with order_by() to get your data grouped together.
from django.db.models.functions import TruncWeek
weeks = EventItem.objects.annotate(week=TruncWeek('StartTime').order_by('week')

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)

Count different values of user over a period of time with selecting the latest for that day

I was trying to get models value over a period of time in django. The model is used to keep kind of activity log.
class Activity(models.Model):
PLACE_CHOICES = (('home', 'Home'),('office', 'Office'))
userId = models.IntegerField()
date = models.DateField()
place = models.CharField(max_length=25, choices=PLACE_CHOICES)
An user can have multiple place for a day ('place' can be duplicate) but I need only the latest model for that day.
I need to group data over a period of time (say 15 days) for multiple user, filtering args are something like this,
Activity.objects.filter(userId__in=[1,2], date__gte='2022-01-01', date__lte='2022-01-15')
This is only to show the filtering. I tried other methods,like-
Activity.objects.filter(
userId__in=[1,2], date__gte='2022-01-01', date__lte='2022-01-15'
).annotate(
home=Count('place', distinct=True, filter=Q(place='home'),
office=Count('place', distinct=True, filter=Q(place='home')
).values('userId','home','office')
I need values like this
[{userId:1, home:4, office:3},{userId:2, home:1, office:3}]

Django filter model with timestamp

I have the following models:
class User(models.Model):
id = models.CharField(max_length=10, primary_key=True)
class Data(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
timestamp = models.IntegerField(default=0)
Given a single user, I would like to know how I can filter using timestamp. For example:
Obtain the data from user1, between now and 1 hour ago.
I have the current timestamp with now = time.time(), also I have 1 hour ago using hour_ago = now-3600
I would like to obtain the Data that has a timestamp between these two values.
Use range to obtain data between two values.
You can use range anywhere you can use BETWEEN in SQL — for dates, numbers and even characters.
e.g.
Data.objects.filter(timestamp__range=(start, end))
from docs:
import datetime
start_date = datetime.date(2005, 1, 1)
end_date = datetime.date(2005, 3, 31)
Entry.objects.filter(pub_date__range=(start_date, end_date))
You can use __gte which is termed as greater than or equal to and __lte refers less than or equal toSo try this,
Data.objects.filter(timestamp__gte=hour_ago,timestamp__lte=now)
You can find similar examples in official doc

How to filter latest objects using nested query in Django

I have the following relation:
class Product(foo):
name = models.CharField()
class Maintenance(foo):
product = models.ForeignKey(Product, related_name="maintenances")
start = models.DateField()
end = models.DateField()
I would like to filter all products with the latest (only the latest) maintenance object having start and end attributes in a given date range.
Something like this:
Product.objects.filter(maintenances__last__end__gte=today.now(), maintenances__last__end__lte=today.now()+datetime.timedelta(days=30))
You could filter the products on the selected range of dates for maintenances and then take the lastest maintenance using annotation on Max:
import datetime as dt
from django.db.models import Max
start_date = dt.datetime.now()
end_date = dt.datetime.now() + dt.timedelta(days=30)
products = Product.objects.filter(maintenances__start__gte=start_date, maintenances__end__lte=end_date)\
.annotate(most_recent_maint=Max('maintenances__id'))\
.prefetch_related('maintenances')
In some cases it also might make sense to think the other way round: Select the latest Maintenance object for every product:
# filter for time range
maintenances = Maintenance.objects.filter(
end__gte=today.now(),
end__lte=today.now() + datetime.timedelta(days=30)
)
# get latest with distinct product id
maintenances = maintenances.order_by(
'product_id', '-end'
).distinct('product_id')
# do a `select_related` to get all products in the same query
maintenances = maintenances.select_related('product')
Note that passing arguments to distinct() only works if you are using PostgreSQL.