【问题标题】:Annotate based on related field, with filters基于相关字段的注释,带有过滤器
【发布时间】:2021-12-20 06:02:46
【问题描述】:
class IncomeStream(models.Model):
    product = models.ForeignKey(Product, related_name="income_streams")
    from_date = models.DateTimeField(blank=True, null=True)
    to_date = models.DateTimeField(blank=True, null=True)
    value = MoneyField(max_digits=14, decimal_places=2, default_currency='USD')
    
    
class Product(models.Model):
    ...
    
    
class Sale(models.Model):
    product = models.ForeignKey(Product, related_name="sales")
    created_at = models.DateTimeField(auto_now_add=True)
    ...
    

使用上述模型,假设我想使用.annotate 为一些Sales 添加一个值。

此值称为cpa(每次操作成本):cpa 是IncomeStream 的值,其from_dateto_date 在其范围内包括销售created_at

此外,from_date 和 to_date 都可以为空,在这种情况下,我们假设它们意味着无穷大。

例如:

<IncomeStream: from 2021-10-10 to NULL, value 10$, product TEST>
<IncomeStream: from NULL to 2021-10-09, value 5$, product TEST>

<Sale: product TEST, created_at 2019-01-01, [cpa should be 5$]>
<Sale: product TEST, created_at 2021-11-01, [cpa should be 10$]>

我的问题是:是否可以仅使用 Django ORM 和注释来编写所有这些条件?如果是,怎么做?

我知道 F 对象可以像这样遍历关系:

Sale.objects.annotate(cpa=F('product__income_streams__value'))

但是我究竟可以在哪里编写所有逻辑来确定它应该从哪个特定的income_stream 中选择value

请假设同一产品的收入流没有重叠的日期,因此上述规格不会导致冲突。

【问题讨论】:

    标签: django django-orm


    【解决方案1】:

    这样的事情应该让你开始

        subquery = (
            IncomeStream
            .objects
            .values('product') # group by product primary key i.e. product_id
            .filter(product=OuterRef('product'))
            .filter(from_date__gte=OuterRef('created_at'))
            .filter(to_date__lte=OuterRef('created_at'))
            .annotate(total_value=Sum('value'))
        )
    

    然后用子查询

        Sale
        .objects
        .annotate(
            cpa=Subquery(
                subquery.values('total_value')
            ) # subquery should return only one row so
            # so just need the total_value column
        )
    

    没有机会自己在 shell 中玩这个,我不是 100%。无论如何,它应该很接近。

    【讨论】:

    • 哇,正是我想要的!这并不能解决可空的 from_date 和 to_date 问题,但这是一个巨大的飞跃。只是提醒一下,我在测试时对您的答案进行了一些编辑。非常感谢!
    • 没问题。如果这还没有涵盖这些情况,您将需要使用 Case 和 When 来处理其他逻辑可能性。
    猜你喜欢
    • 1970-01-01
    • 2013-10-30
    • 1970-01-01
    • 1970-01-01
    • 2018-12-23
    • 2020-03-11
    • 2020-08-15
    • 1970-01-01
    • 2012-04-05
    相关资源
    最近更新 更多