问题
Let's assume I have a Product
model in my project:
class Product(models.Model):
price = models.IntegerField()
and I want to have some sort of statistics (let's say I want to keep track of how has the price changed over time) for it:
class ProductStatistics(models.Model):
created = models.DateTimeField(auto_add_now=True)
statistics_value = models.IntegerField()
product = models.ForeignKey(Product)
@classmethod
def create_for_product(cls, product_ids):
statistics = []
products = Product.objects.filter(id__in=products_ids)
for product in products:
statistics.append(
product=product
statistics_value=product.price
)
cls.objects.bulk_create(statistics)
@classmethod
def get_latest_by_products_ids(cls, product_ids):
return None
I have a problem with implementing get_latest_by_products_ids
method. I want only latest statistic, so I can't do something like:
@classmethod
def get_latest_by_products_ids(cls, product_ids):
return cls.objects.filter(product__id__in=product_ids)
because this would return all statistics I have gathered through time. How can I limit the query to only most recent one for each Product?
EDIT I am using PostgreSQL database.
回答1:
Querysets already have a last() method (and a first()
method too FWIW). The only question is what you want to define as "last" since this depends on the queryset's ordering... But assuming you want the last by creation date (created
field), you can also use the lastest() method:
@classmethod
def get_latest_by_products_ids(cls, product_ids):
found = []
for pid in products_ids:
found.append(cls.objects.filter(product_id=pid).latest("created"))
return found
As a side note: Django's coding style is to use the Manager (and eventually the Queryset) for operations working on the whole table, so instead of creating classmethods on your model you should create a custom manager:
class productStatisticManager(models.Manager):
def create_for_products(self, product_ids):
statistics = []
products = Product.objects.filter(id__in=products_ids)
for product in products:
statistics.append(
product=product
statistics_value=product.price
)
self.bulk_create(statistics)
def get_latest_by_products_ids(cls, product_ids):
found = []
for pid in products_ids:
last = self.objects.filter(product_id=pid).latest("created")
found.append(last)
return found
class ProductStatistics(models.Model):
created = models.DateTimeField(auto_add_now=True)
statistics_value = models.IntegerField()
product = models.ForeignKey(Product)
objects = ProductStatisticManager()
回答2:
Putting the method in Product model and will be easier:
class Product(models.Model):
price = models.IntegerField()
def get_latest_stat(self):
return self.productstatistics_set.all().order_by('-created')[0] # or [:1]
Using [:1] instead of [0] will return QuerySet of single element while [0] will return just one Object of model Class.
eg.
>>> type(cls.objects.filter(product__id__in=product_ids).order_by('-created')[:1])
<class 'django.db.models.query.QuerySet'>
>>> type(cls.objects.filter(product__id__in=product_ids).order_by('-created')[0])
<class 'myApp.models.MyModel'>
来源:https://stackoverflow.com/questions/52586866/django-get-latest-object-in-each-relation