Django get last value for each forgin key values - python

In models.py
class loan(models.Model):
completed=models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True,null=True)
application_id=models.CharField(max_length=100,unique=True)
class topay(models.Model):
loanapplication=models.ForeignKey(loan,on_delete=models.CASCADE,null=True)
paymentdate=models.DateField(null=True, blank=True)
How to get last paymentdate for every loan application
In views.py
topay.objects.filter().values().annotate(last=Max('paymentdate'))

to get each loan latest topay you can filter like this
loan.objects.annotate(
maxpaydate=Max("topay__paymentdate"),
).values("id", "maxpaydate")

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.

Not able to access related model data using foreign key in Django

models.py
class products(models.Model):
name = models.CharField(max_length=100)
sku = models.CharField(max_length=50)
vendor = models.CharField(max_length=50)
brand = models.CharField(max_length=50)
price = models.FloatField()
product_status = models.BooleanField()
quantity = models.IntegerField()
def __str__(self):
return self.name
# categories
class categories(models.Model):
category_name = models.CharField(max_length=50)
parent_id = models.IntegerField()
# product categories
class product_categories(models.Model):
product = models.ForeignKey(products, on_delete=models.CASCADE)
category = models.ForeignKey(categories, on_delete=models.CASCADE)
def __str__(self):
return self.category
I can access 'category' table data(inside django shell) using
data = products.objects.all()
data.values('product_categories__category__category_name')
output: <QuerySet [{'product_categories__category__category_name': 'xxxx'}}]>
If I put this(inside django shell)
data.product_categories.category
output: 'QuerySet' object has no attribute 'product_categories'
How do I get a queryset(can be passed to html) which includes data from "categories" table along with the data of "products" table
There are a couple of issues happening here. First, data is a queryset, which is kind of like a list of objects, even though here there's just one object in the list. What you want is to get an attribute off of the item in the list, so you need something like a data.first() to get to that object before you start dotting into its attributes.
Secondly, the way Django handles reverse FK relationships requires that you refer to the FK by the standard name of, in your case, product_categories_set, OR you set your own related_name attribute on the FK. Something like:
# product categories
class product_categories(models.Model):
product = models.ForeignKey(products, on_delete=models.CASCADE, related_name='product_categories')
category = models.ForeignKey(categories, on_delete=models.CASCADE, related_name='product_categories')
def __str__(self):
return self.category
so that you can refer to your product_categories model from both the product and categories using just data.product_categories.
Thirdly, when accessing a reverse FK relationship, just like in point (1) above, you will get a related manager, from which you can get a queryset of items. Thus, to get the category name, you need to indicate which item you want the category name for. Assuming it's just the first item for everything, it would look something like:
data = products.objects.all()
product_category = data.product_categories.all()
category_name = product_category.category.category_name
Of course once you have more data, you'll not always want to just pick the first item, so you'll need to add filtering logic into the query to make sure you get the item you're looking for.
ETA, I do agree with the comment by Jorge above - a MTM would make this a bit simpler and would, in essence, create your product_categories table for you.

Django Beginner: Display value of RawQuerySet

I am using a RAW SQL query to get the record of each week's sale and then sum it up and show it on the web page. Here's my query:
sales = addCustomer.objects.raw("SELECT SUM(productPrice) FROM cms_addcustomer WHERE date <= %s and date >= %s", [startdate, weekDate])
Already using the datetime library to get the date and everything is working fine on this end.
However, when I try to display the result I get this message on my webpage:
Total Sales: Rs.<RawQuerySet: SELECT
SUM(productPrice) FROM cms_addcustomer WHERE
date <= 2020-07-29 and date >= 2020-07-23>
As also shown in this screenshot
I just want to display the sum of the Sales on my webpage but I am not sure how to do it.
addCustomer Model:
from django.db import models
from datetime import date
class addCustomer(models.Model):
customerName = models.CharField(max_length=100)
productName = models.CharField(max_length=100)
productPrice = models.IntegerField()
date = models.DateField(default=date.today)
commission = models.IntegerField()
I have read most of the answers on Stack Overflow but I was unable to understand them as I am complete beginner. How can I fix this?
You can create a query with .aggregate(…) [Django-doc]:
from django.db.models import Sum
total = addCustomer.objects.filter(
date__range=(weekDate, startDate)
).aggregate(
total_productprice=Sum('productPrice')
)['total_productprice']
here total will thus retrieve the sum of the productPrice values in the range.
For your date field, you might want to use a db_index=True [Django-doc] to boost searching the records. Furthermore you might want to use auto_now_add=True [Django-doc], this will not only set the field value to the current date, but also make the field non-editable by default:
class addCustomer(models.Model):
customerName = models.CharField(max_length=100)
productName = models.CharField(max_length=100)
productPrice = models.IntegerField()
date = models.DateField(auto_now_add=True, db_index=True)
commission = models.IntegerField()
Note: normally the name of the fields in a Django model are written in snake_case, not PerlCase, so it should be: product_price instead of productPrice.

Django count with group in annotation

I have a profile model with a ManyToMany field called that relates to a Stock. In my admin dashboard I'm trying to show the number of watchlists each stock is in. The annotation query I have is:
qs.annotate(watchlist_count=Count('profile__watchlist__symbol'))
But it's returning incorrect results
Here are the models:
class Profile(models.Model):
user = OneToOneField(User, on_delete=models.CASCADE)
watchlist = ManyToManyField(Stock, blank=True)
def __str__(self):
return self.user.email
class Stock(models.Model):
symbol = CharField(max_length=15, unique=True)
name = CharField(max_length=100)
category = CharField(max_length=30, choices=CATEGORY_CHOICES, blank=True)
about = TextField(help_text='About this company')
def __str__(self):
return f'{self.symbol} - {self.name}'
The equivalent SQL query is:
select stock_id, count(stock_id) from api_profile_watchlist group by stock_id;
What is wrong with my annotation query?
You do too much joins. By joining twice over the many-to-many relation, you "blow up" the count.
You can simply count the amount of watchlists with that Stock, with:
from django.db.models import Count
Stock.objects.annotate(
watchlist_count=Count('profile')
)
This works since, by default, the related_query_name=… [Django-doc] has the name of the model (or the related_name if you specified one). So the implicit relation you wrote from Stock to Profile (the reverse one of Profile to Stock in your watchlist relation), is profile (in lowercase). We thus ask Django to count, for a given Stock object, the number of relations to a Profile.

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