Django Filter by date and status - python

My first question here, I've been looking around but couldn't find a solution.
I am building a reservation system, my models are
class Room(models.Model):
number = models.IntegerField()
beds = models.IntegerField()
capacity = models.IntegerField()
category = models.ForeignKey(
RoomCategory, on_delete=models.CASCADE, blank=True, null=True)
class Booking(models.Model):
room = models.ForeignKey(Room, on_delete=models.CASCADE, blank=True, null=True)
check_in = models.DateField()
check_out = models.DateField()
status = models.IntegerField(blank=True, null=True)
if request.method == 'POST':
no_disponible = "No hay habitaciones disponibles, selecciona otras fechas"
if form.is_valid():
room_list = Room.objects.filter(category=1).exclude(booking__check_in__lt=form.cleaned_data['check_out'],
booking__check_out__gt=form.cleaned_data['check_in'], booking__status__gt=0)
I am changing status when the customer confirms so I want to check if dates are available when status is not 1 (I change status to 1 once the payment is approved.
However booking__status__gt=0 doesn't seem to work here

Based on your query, you want to exclude rooms with:
Bookings with check ins that are less than the requested check out.
Bookings with check outs that are greater than the requested check in.
Bookings with status greater than 0.
Your current query is:
Room.objects.filter(category=1).exclude(
booking__check_in__lt=form.cleaned_data['check_out'],
booking__check_out__gt=form.cleaned_data['check_in'],
booking__status__gt=0,
)
This will remove all rooms that have status greater than 0, as well as rooms with invalid checkin/checkout dates, even though some of those rooms with invalid checkin/checkout dates have status as 0. This is because of how exclude works when it spans multi-valued relationships like this.
To fix this, change your query to get all the bookings with the exclusions you want by using Booking, and then compare it against the reverse relationship:
Room.objects.filter(category=1).exclude(
booking__in=Booking.objects.filter(
check_in__lt=form.cleaned_data['check_out'],
check_out__gt=form.cleaned_data['check_in'],
status__gt=0,
)
)
Have a read here to understand the behaviour of exclude in this case more.

Related

Django: query filter

I have two models that are related: one is a list of participants. The other is a list of times they have checked in or out of an office.
The table (Checkin) has one record for every checkin/checkout pair. So, there can be many records for any participant.
How can I retrieve only the very last (most recent) record for a participants checkin and then pass the participant and only that most recent Checkin record to my template?
From what I can tell there's no ability to do something like a last() in my template, so how would I go about filtering to get just that single record?
Thank you.
Models:
class Participant(models.Model):
first_name = models.CharField(max_length=50)
middle_initial = models.CharField(max_length=50, blank=True)
class CheckIn(models.Model):
adult = models.ForeignKey(
Participant, on_delete=models.CASCADE, blank=True, null=True, related_name='adult_checkin')
checkin = models.DateTimeField(blank=True, null=True)
checkout = models.DateTimeField(blank=True, null=True)
View snipit:
p_checkins = Participant.objects.all().order_by('created')
queryset = p_checkins
context_object_name = "the_list"
template_name = 'list_of_checkins.html'
You can fetch data through most recent checkin or checkout.
For checkin :
p_checkins = CheckIn.objects.all().order_by('-checkin')[0]
For checkout :
p_checkins = CheckIn.objects.all().order_by('-checkout')[0]
To get the participant name by :
name = p_checkins.adult.first_name
When you use (-) your latest update will be query from database.
p_checkins = CheckIn.objects.all().order_by('-checkin')
or
p_checkins = CheckIn.objects.all().order_by('-checkout')
you can annotate the latest value via a subquery to the participant
from django.db.models import OuterRef, Subquery
checkin_q = CheckIn.objects.filter(adult=OuterRef('pk')).order_by('-checkin')
queryset = Participant.objects.annotate(last_checkin=Subquery(checkin_q.values('checkin')[:1]))
see https://docs.djangoproject.com/en/4.0/ref/models/expressions/#subquery-expressions
Most of the answers so far are correct in several aspects. One thing to note is that if your check_in or check_out values (whichever you use) isn't chronological (and by "most recent", you mean the last added), you'll want to add a created_at datetime field with auto_now option True, or order by the pk.
In addition to the other answers provided and my comment above, you can also get the most recent check in by using the related manager on the participant object.

Django Fast Access for Time Series Data

I'm working on a web application with Django & PostgreSQL as Backend tech stack.
My models.py has 2 crucial Models defined. One is Product, and the other one Timestamp.
There are thousands of products and every product has multiple timestamps (60+) inside the DB.
The timestamps hold information about the product's performance for a certain date.
class Product:
owner = models.ForeignKey(AmazonProfile, on_delete=models.CASCADE, null=True)
state = models.CharField(max_length=8, choices=POSSIBLE_STATES, default="St.Less")
budget = models.FloatField(null=True)
product_type = models.CharField(max_length=17, choices=PRODUCT_TYPES, null=True)
name = models.CharField(max_length=325, null=True)
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name="children")
class Timestamp:
product = models.ForeignKey(Product, null=True, on_delete=models.CASCADE)
product_type = models.CharField(max_length=35, choices=ADTYPES, blank=True, null=True)
owner = models.ForeignKey(AmazonProfile, null=True, blank=True, on_delete=models.CASCADE)
clicks = models.IntegerField(default=0)
spend = models.IntegerField(default=0)
sales = models.IntegerField(default=0)
acos = models.FloatField(default=0)
cost = models.FloatField(default=0)
cpc = models.FloatField(default=0)
orders = models.IntegerField(default=0)
ctr = models.FloatField(default=0)
impressions = models.IntegerField(default=0)
conversion_rate = models.FloatField(default=0)
date = models.DateField(null=True)
I'm using the data for a dashboard, where users are supposed to be able to view their products & the performance of the products for a certain daterange inside a table.
For example, a user might have 100 products inside the table and would like to view all data from the past 2 weeks. For this scenario, I'll describe the code's proceedure below:
Make call to the backend / server
Server has to filter & aggregate all Timestamps for each Product
Server sends data back to client
Client updates table values
The problem is, that step 2. takes a huge amount of time, and I do not know how to improve the performance.
products = Product.objects.filter(name="example")
for product in products:
product.report_set.filter(date_gte="2021-01-01", date__lte="2011-01-14").aggregate(
Sum("clicks"),
Sum("cost"),
Sum("sales"))
That is how the server is currently retrieving the timestamp values for the displayed products.
Any ideas how to retrieve & structure the data in a more efficient way?
It's slow because of the multiple queries you need to make to the database (in the loop).
See if grouping and annotating is better(one query then perhaps queries for fetching each product):-
Timestamp.objects.filter(daterange=["2011-01-01", "2011-01-15"]).values('product').annotate(sum_clicks=Sum("clicks")).annotate(sum_cost=Sum("cost")).annotate(sum_sales=Sum("sales"))
I don't know if this is possible but if it is it would be even better:-
Timestamp.objects.filter(daterange=["2011-01-01", "2011-01-15"]).values('product').annotate(sum_clicks=Sum("clicks")).annotate(sum_cost=Sum("cost")).annotate(sum_sales=Sum("sales")).select_related('product')
Edit:-
After looking back perhaps this might be better:-
products = Product.objects.filter(name="example", report_set__daterange=["2011-01-01", "2011-01-15"]).annotate(sum_clicks=Sum("report_set__clicks")).annotate(sum_cost=Sum("report_set__cost")).annotate(sum_sales=Sum("report_set__sales"))
Without more detail all i can recommend is to optimize, for database optimization i would follow the instructions listed here but know as you speed up the query there will likely be an increase in memory usage.

Django: Queryset filtering based on min max set in another table

Consider the following models:
Class ExamResults:
...
Marks = models.IntegerField(default=0, null=True, blank=True)
Course = models.CharField(max_length=255, null=True)
Academic_year = models.CharField(max_length=255, null=True)
Class ExamSetting:
...
GradeAThreshold = models.IntegerField(default=0, null=True, blank=True)
GradeBThreshold = models.IntegerField(default=0, null=True, blank=True)
...
Course = models.CharField(max_length=255, null=True)
Academic_year = models.CharField(max_length=255, null=True)
Now I have an API to search/get results from ExamResults for all students. The API works fine and I use Q filters to filters the results. For e.g.
...
year_contains = self.request.GET.get("year_contains", "")
if year_contains:
q_filt |= Q(Academic_year__icontains=year_contains)
queryset = queryset.filter(q_filt)
...
Now I have a requirement to filter the queryset for the following condition:
List of exam results where the Marks exceed GradeAthreshold
List of exam results where the Marks are less than GradeAthreshold and exceed GradeBThreshold and so on
What would be the best way of doing this? The ExamResults table and ExamSetting has two common fields which can narrow down the thresholds. For e.g. I use the below code in serializer to check if the result has Grade A or not:
setting = ExamSetting.objects.filter(Academic_year=obj.Academic_year, Course=obj.Course, is_active=True).first()
if obj.Marks >= setting.GradeAThreshold:
# Grade A
...
This does work and I do get the results with grades. Now how do I add something like this in the queryset so that I can filter the results for Grade A or B results?.
As you mentioned ExamSetting and ExamResult both have common fields; assuming proper data integrity, you can pivot based on one of those.
For example, if you want all exam results with grade A or above for a similar course:
setting = ExamSetting.objects.filter(
Academic_year=obj.Academic_year,
Course=obj.Course,
is_active=True
).first()
query = Q(Course=setting.Course) & Q(Mark__gte=setting.GradeAThreshold)
ExamResults.objects.filter(query)

Need help in Django Orm query

I have 3 models and they are as follow
class Table(models.Model):
waiter = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,
related_name='restaurant_table')
table_no = models.IntegerField()
objects = TableManager()
class Order(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
food = models.ManyToManyField(OrderFood, related_name='ordered_food')
order_status = models.ForeignKey(OrderStatus, on_delete=models.CASCADE)
table = models.ForeignKey(Table, on_delete=models.CASCADE)
datetime = models.DateTimeField(default=now)
class OrderStatus(models.Model):
CHOOSE = (
('Received', 'Received'),
('Cooking', 'Cooking'),
('WaiterHand', 'In Waiter Hand'),
('Delivered', 'Delivered'),
('Paid', 'Payment Completed'),
('Rejected', 'Rejected')
)
status = models.CharField(max_length=30, null=False, blank=False, choices=CHOOSE)
created_at = models.DateTimeField(auto_now=True)
updated_at = models.DateTimeField()
Actually I am creating a restaurant management system. So here a restaurant has tables associated with a or more waiter. But I need a new feature that is table status. I mean when an order is actively associated with the table that means that table is booked. Actually that is not a problem as I can do that in many ways.
One way is I will count the active order associated with this table and if I found any active order I will return the table is booked.
Another way is I will add an extra field with the table that is a flag. This flag store status of tables is booked or not I mean the boolean field.
But my question is not the solution. My question which one is better or there are any other good solutions. Please explain it briefly I want to know which solution is better and why.
you can put #property function under class Table which you can use directly with any table objects, in templates also.
#property
def check_table_status(self):
status = 'Not Booked'
if self.order_set.all().exists():
status = 'Booked'
return status

Queryset difference based on one field

I am trying to compare two querysets based on a single field. But I can't figure out most efficient way to do it.
This is my model and I want to check if old and new room_scans(ForeignKey) has PriceDatum's with the same checkin date. if not, create PriceDatum with that checkin date related to the new room_scan.
class PriceDatum(models.Model):
"""
Stores a price for a date for a given currency for a given
listingscan
Multiple such PriceData objects for each day for next X months are created in each Frequent listing scan
"""
room_scan = models.ForeignKey(RoomScan, default=1, on_delete=models.CASCADE)
room = models.ForeignKey(Room, on_delete=models.CASCADE)
checkin = models.DateField(db_index=True, help_text="Check in date", null=True)
checkout = models.DateField(db_index=True, help_text="checkout date", null=True)
price = models.PositiveSmallIntegerField(help_text="Price in the currency stated")
refund_status = models.CharField(max_length=100, default="N/A")
# scanned = models.DateTimeField(db_index=True, help_text="Check in date", null=True)
availability_count = models.PositiveSmallIntegerField(help_text="How many rooms are available for this price")
max_people = models.PositiveSmallIntegerField(help_text="How many people can stay in the room for this price")
meal = models.CharField(max_length=100, default="N/A", help_text="Tells if breakfast is included in room price")
Below is the code what I am trying to do:
previous_prices_final = previous_prices.filter(refund_status='refund',
current_prices_final=current_prices.filter(
refund_status='refund', max_people=max_people_count, meal=meal).order_by().order_by('checkin')
if len(previous_prices_final) > len(current_prices_final):
difference=previous_prices_final.difference(current_prices_final)
for x in difference:
PriceDatum.objects.create(room_scan=x.room_scan,
room=x.room,
checkin=x.checkin,
checkout=x.checkout,
price=0,
refund_status='refund',
availability_count=0,
max_people=max_people_count,
meal='not_included',
)
The thing is that I get all queries as different, because room_scan foreign key has different time created.
My question is: How do I use difference(), based only on checkin field.
Don't select field that contains creating time. Limit your QS with values.

Categories