【问题标题】:Django annotate queryset on a specific value of relational model attributeDjango在关系模型属性的特定值上注释查询集
【发布时间】:2016-01-13 21:33:06
【问题描述】:

假设有这样的结构:

PARTICIPATION_STATUSES = (
    (0, 'No, thanks'),
    (1, 'I may attend'),
    (2, 'I\'ll be there'),
)

class Model1(models.Model):
    # ...

class Model2(models.Model):
    status = models.PositiveIntegerField(
        _('participation status'), choices=PARTICIPATION_STATUSES)    
    field = models.ForeignKey(Model1, related_name='model1_participation')

我想要做的是用Model2 对象的计数来注释Model1 的每个对象,其中状态等于特定值(状态编号是这个特定的示例)。

在我的伪代码中它看起来像:

queryset = Model1.objects.all()
queryset.annotate(declined=Count('model1_participation__status=0'))
queryset.annotate(not_sure=Count('model1_participation__status=1'))
queryset.annotate(accepted=Count('model1_participation__status=2'))

但我不能以这种方式注释查询集,因为 Django 无法解析 status=<n>。

什么是实现我想要的正确方法?

【问题讨论】:

    标签: python django django-models


    【解决方案1】:

    如果您使用的是 Django 1.8 或更高版本,则可以使用 Conditional Aggregations,这些应该适用于 annotate 查询集。

    from django.db.models import IntegerField, Case, When, Count
    
    
    queryset = Model1.objects.all()
    
    queryset = queryset.annotate(
        declined=Count(
            Case(When(model1_participation__status=0, then=1),
                 output_field=IntegerField())
        ),
        not_sure=Count(
            Case(When(model1_participation__status=1, then=1),
                 output_field=IntegerField())
        ),
        accepted=Count(
            Case(When(model1_participation__status=2, then=1),
                 output_field=IntegerField())
        )
    )
    

    【讨论】:

    • 谢谢,这看起来很不错,但我需要注释对象而不是聚合。你对这样的注释有什么想法吗?
    • 我已经修改了答案,它应该仍然以同样的方式工作。试一试,告诉我进展如何!
    • @pythad here 你可以自己看看如何对相关模型执行查询。例如Reporter.objects.filter(article__pk=1) 与您的模型将是 Model1.objects.filter(model2__status=1)。因此,如果您使用的是_set,则意味着您忘记提及其他一些代码(即查询是在“model1”的特定记录/实例上执行的。可能是迭代?)
    • @Pynchia,我稍微简化了我的问题。我的项目中有一个related_name,我查询它。只是用_set弄乱了相关名称。对不起,谢谢您的指点。
    • 好的,那么为了让问题对其他人有帮助,请编辑问题中的模型并将related_name 参数添加到Model2 中的field。您的问题非常相关并且没有很多有用的答案(最近的答案)。所以你得到的答案对 Django 社区确实很有用
    【解决方案2】:

    您可以使用 Exists 子查询:

    from django.db.models.expressions import Exists, ExpressionWrapper, OuterRef, Subquery, Value
    from django.db.models.fields import BooleanField
    
    queryset = Model1.objects.all()
    queryset.annotate(
      declined=ExpressionWrapper(
        Exists(Model2.objects.filter(                        
          field=OuterRef('id'),
          status=0)),
        output_field=BooleanField()))),
      not_sure=ExpressionWrapper(
        Exists(Model2.objects.filter(
          field=OuterRef('id'),
          status=1)),
        output_field=BooleanField()))),
      accepted=ExpressionWrapper(
        Exists(Model2.objects.filter(                        
          field=OuterRef('id'),
          status=2)),
        output_field=BooleanField())))
    )
    

    为了使其更清晰/可重用,您可以重构为一个函数:

    def is_status(status_code):
       return ExpressionWrapper(
         Exists(Model2.objects.filter(                        
            field=OuterRef('id'),
            status=status_code)),
         output_field=BooleanField())))
    
    Model1.objects.annotate(
      declined=is_status(0),
      not_sure=is_status(1),
      accepted=is_status(2)
    )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-07-31
      • 2020-11-14
      • 2019-01-13
      • 1970-01-01
      • 2021-01-27
      • 2013-03-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多