【问题标题】:How to annotate a django queryset with a function of prefetched subquery如何使用预取子查询的功能注释 django 查询集
【发布时间】:2020-05-17 19:12:57
【问题描述】:

我想在一个查询集上有一个字段status,它是从一组预取的反馈值的函数派生的。

反馈示例:

feedback = [
    {'action': 0, 'user': 1},
    {'action': 1, 'user': 13},
]

如果我在序列化器上写这个,我会这样做:

    def get_status(self, obj):
        # Squash all feedback into single status value
        fb = obj.feedback.all()
        vals = [f.action for f in fb]
        if len(vals) == 0:
            return 0  # Unconfirmed
        if sum(vals) == 0:
            return 1 # Confirmed
        return 2  # Rejected

但是我想将此逻辑下移到我的视图的查询集中以启用字段排序。

queryset = Foo.objects\
        .prefetch_related('feedback')\
        .annotate(status="???")

我想知道哪组可用的查询表达式可以模仿上面的python函数get_status的逻辑。

【问题讨论】:

  • 您不能在.annotate(..) 中调用函数,因为数据库层不知道函数。您可以尝试将上述内容转换为表达式。顺便说一句,您不需要为此需要.prefetch_related(..)

标签: python django-models django-rest-framework


【解决方案1】:

您不能在.annotate(..) 中调用函数,因为数据库层不知道函数。您可以尝试将上述内容转换为表达式。您确实不需要为此需要.prefetch_related(..)。这里可以使用Case [Django-doc]When [Django-doc]

from django.db.models import Case, Count, IntegerField, Sum, Value, When

queryset = Foo.objects.annotate(
    nfeedback=Count('feedback'),
    sumfeedback=Sum('feedback__action')
).annotate(
    status=Case(
       When(nfeedback=0, then=Value(0)),
       When(sumfeedback=0, then=Value(1)),
       default=Value(2),
       output_field=IntegerField()
    )
)

【讨论】:

  • 这解决了我的问题。 (非常感谢您准确快速的回答)为了清楚起见,如果我使用 prefetch_related(),这不会导致每行的数据库命中吗?
  • @IanDanforth: 不是为了注解,从那时起 Django 会进行 JOIN。 .select_related(..).prefetch_related(..) 应该在获取相关模型数据到 Python/Django 层时使用。但这会在数据库中进行 JOIN 和聚合,因此会导致 one 查询。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-30
  • 2021-09-17
  • 1970-01-01
  • 2011-04-08
  • 2019-12-30
相关资源
最近更新 更多