Django: query filter - python

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.

Related

Django: how to use .filter( ) method in django?

I am trying to display quiz only for users that are registered in a particular course, i.e if a user is registered in a Frontend Crash Course i want them to see only the quiz related to that course they are registered in, and not all the quiz from the db.
i have a model UserCourse where i am storing all the courses a user have enrolled in, when i try filtering by that models while user_course is get like this below
user_course = UserCourse.objects.get(user=request.user)
quizzes = Quiz.objects.filter(course__usercourse=user_course).annotate(questions_count=Count('questions'))
i get this error get() returned more than one UserCourse -- it returned 3! Now i have changed .get() to .filter() like this
user_course = UserCourse.objects.filter(user=request.user)
quizzes = Quiz.objects.filter(course__usercourse=user_course).annotate(questions_count=Count('questions'))
i then get this error The QuerySet value for an exact lookup must be limited to one result using slicing.
What is the right way to write this query.
models.py
class UserCourse(models.Model):
user = models.ForeignKey(User , null = False , on_delete=models.CASCADE)
course = models.ForeignKey(Course , null = False , on_delete=models.CASCADE, related_name="usercourse")
class Quiz(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="quizzes")
title = models.CharField(max_length=255)
course = models.ForeignKey(Course, on_delete=models.SET_NULL, null=True, related_name="quizzes")
date = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(unique=True)
user_course = models.ForeignKey(UserCourse, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.title
The Problem in the Second Line
user_course = UserCourse.objects.filter(user=request.user)
quizzes=Quiz.objects.filter(course__usercourse=user_course).annotate(questions_count=Count('questions'))
remember that when You are using filter you get QuerySet not one object
if you want to return the quizes those related to user_course_queryset you can use __in filter
print(user_course) # print it to understand more
quizzes=Quiz.objects.filter(course__usercourse__in=user_course)
this will Return every Quiz Related to the QuerySet objects

django filter get parent to child

i am using django(3.1.5). and i am trying to get parent model to child model by filter query
i have model like -
class Product(models.Model):
product_name = models.CharField(max_length=255, unique=True)
is_feature = models.BooleanField(default=False)
is_approved = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
class ProductGalleryImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
product_gallery_image = models.FileField(upload_to='path')
is_feature = models.BooleanField(default=False)
i am getting data from SELECT * FROM products_product AS pp INNER JOIN products_productgalleryimage AS ppgi ON ppgi.product_id = pp.id WHERE ppgi.is_feature=1 AND pp.is_feature=1 AND is_approved=1 ORDER BY pp.created_at LIMIT 4 mysql query.
so how can i get data like this query in django filter query
Firstly you can add related_name to ProductGalleryImage for better query support like this
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='product_images')
Then your query should be like this
products=Product.objects.filter(is_approved=True, is_feature=True, product_images__is_feature=True).order_by('created_at')[:4]
You can simply loop over the other related model like so:
for product_gallery_image in product_instance.productgalleryimage_set.all():
print(product_gallery_image.product_gallery_image)
The productgalleryimage_set here is simply the related model name in lowercase with _set appended. You can change this by setting the related_name attribute on the foreign key.
Note: This will perform a query to fetch each of the product_gallery_image objects of some product instance.
If you want to get the first object only:
product_gallery_image = product_instance.productgalleryimage_set.first()
If you want to perform a join as in your example which will perform only one query you can use select_related (this will only work in forward direction for reverse direction look at prefetch_related):
product_gallery_images = ProductGalleryImage.objects.all().select_related('product')
for product_gallery_image in product_gallery_images:
print(product_gallery_image.product.product_name)
print(product_gallery_image.product_gallery_image)

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

Joining and iterating through foreign-key tables with Django

I have the following 2 Django models:
from django.db import models
class Stock(models.Model):
symbol = models.CharField(db_index=True, max_length=5, null=False, editable=False, unique=True)
class PriceHistory(models.Model):
stock = models.ForeignKey(Stock, related_name='StockHistory_stock', editable=False)
trading_date = models.DateField(db_index=True, null=False, editable=False)
price = models.DecimalField(max_digits=12, db_index=True, decimal_places=5, null=False, editable=False)
class Meta:
unique_together = ('stock', 'date')
Obviously this leads to two DB tables being created: myapp_stock and myapp_pricehistory. These tables have 2 and 4 columns respectively. The first table contains thousands of rows. The second table contains millions of rows.
I want to join the tables, sort the resultant rows and iterate through these rows one-by-one print them. This is how I plan to do it:
for i in PriceHistory.object.all().order_by('stock__symbol', 'trading_date'):
print '{} {}: {}'.format(i.stock.symbol, i.trading_date, i.price)
Is this the most efficient way to do it to minimize calls to the database? I want it to run only one SQL query. I'm concerned that the above code will run a separate query of the myapp_stock table each time it goes through the for loop. Is this concern valid? If so, how to avoid that?
Basically, I know the ideal SQL would look something like this. How can I get Django to execute something similar?:
select
s.symbol,
ph.trading_date,
ph.price
from
myapp_stock as s,
myapp_pricehistory as ph
where
ph.stock_id=s.id
order by
s.symbol asc,
ph.trading_date asc
You need to use select_related to avoid making an additional query for each item in the loop:
histories = PriceHistory.objects.all().select_related('stock')\
.order_by('stock__symbol', 'trading_date')
for i in histories:
print '{} {}: {}'.format(i.stock.symbol, i.trading_date, i.price)

Django - Query object by last element added to ManyToMany field

I have something like this:
class Car(models.Model):
model = models.CharField(max_length=200)
brand = models.CharField(max_length=100)
status = models.ManyToMany(Status)
class Status(models.Model):
name = models.CharField(max_length=40)
date = models.DateTimeField('Status creation date')
How can I query all the cars where their last status (most recent) is REPAIRED for instance? Is this achievable in just one query?
from django.db.models import Max
cars = Car.objects.annotate(Max('status__date')).filter(status__name='REPAIRED').distinct()
You may read the Django examples for Many to Many relationships.

Categories