Django - get latest object in each relation

纵饮孤独 提交于 2021-01-29 05:14:48

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!