【问题标题】:Django - get latest object in each relationDjango - 获取每个关系中的最新对象
【发布时间】:2018-10-01 08:03:44
【问题描述】:

假设我的项目中有一个Product 模型:

class Product(models.Model):
    price = models.IntegerField()

并且我想要一些统计数据(假设我想要跟踪价格随时间的变化):

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

我在实现get_latest_by_products_ids 方法时遇到问题。我只想要最新的统计数据,所以我不能这样做:

    @classmethod
    def get_latest_by_products_ids(cls, product_ids):
        return cls.objects.filter(product__id__in=product_ids)

因为这会返回我收集到的所有统计数据。如何将查询限制为每个产品的最新查询?

编辑 我正在使用 PostgreSQL 数据库。

【问题讨论】:

    标签: django postgresql django-orm


    【解决方案1】:

    Querysets already have a last() method(还有一个 first() 方法太 FWIW)。唯一的问题是你想定义什么为“最后一个”,因为这取决于查询集的排序......但假设你想要最后一个创建日期(created 字段),你也可以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
    

    附带说明:Django's coding style is to use the Manager (and eventually the Queryset) for operations working on the whole table,因此您应该创建一个自定义管理器,而不是在您的模型上创建类方法:

    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()
    

    【讨论】:

    • 是否可以在单个查询中获取这些数据,而不是在每次循环迭代中执行它们?
    • @gonczor 你能写出一次性完成的 SQL 查询吗?
    • 我正在浏览文档,似乎 django 可以使用 PostgreSQL 数据库执行类似 DISTINCT ON column_name 的操作(抱歉,我之前没有提到我正在使用它)。似乎这个查询:self.filter(product__pk__in=product_ids).order_by('product', '-created',).distinct( 'product',) 可以完成这项工作,但我仍然需要对其进行测试。无论如何,为风格建议 +1。
    【解决方案2】:

    把方法放在产品模型中会更容易:

    class Product(models.Model):
        price = models.IntegerField()
    
        def get_latest_stat(self):
            return self.productstatistics_set.all().order_by('-created')[0] # or [:1]
    

    使用 [:1] 而不是 [0] 将返回单个元素的 QuerySet,而 [0] 将仅返回模型类的一个对象。

    例如。

    >>> 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'>
    

    【讨论】:

    • 问题是我想要一个 ProductStatistics 对象用于我传递的每个 Product id。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 2022-01-17
    • 2020-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多