如何将查找转换为要在`QuerySet.annotate`中使用的表达式?

时间:2019-01-08 15:02:54

标签: django django-queryset django-orm

主题可能有点模糊,因此这里是一个示例。假设我有一个模型:

class TestModel(models.Model):
    user = models.ForeignKey(User, ...)

我想要一个包含has_user字段的QuerySet,该字段基本上将映射到以下SQL查询

select *, user_id is not null as has_user
from app_testmodel

使用has_user时如何向Django解释此QuerySet.annotate字段?


我知道Django具有models.Fmodels.Qmodels.lookupsmodels.expressions等概念,但是我无法理解如何在我的情况下应用它们。据我所知,这是将“查找”转换为布尔“表达式”的问题,其中

  • 在Django ORM语言中,“查找”类似于'user_id__isnull'lookups.IsNull(models.F('user_id'))

  • “表达式”类似于expressions.ExpressionWrapper(?, output_field=models.BooleanField())

到目前为止,我仅设法将user_id is not null表达式转换为case when user_id is not null then true else false end,该表达式映射为如下的Python代码:

from django.db import models
from django.db.models import expressions

# ...
qs = TestModel.objects.all().annotate(has_user=
    expressions.Case(
        expressions.When(
            user__isnull=False,
            then=expressions.Value(True),
        ),
        default=expressions.Value(False),
        #
        # Tell Django the expected type of the field, see `output_field` in
        # https://docs.djangoproject.com/en/2.1/ref/models/expressions/
        #
        output_field=models.BooleanField()))

但这是一个尴尬的解决方法。必须有一个更好的解决方案:更合适,更轻松,更清洁到Python代码以及生成的SQL查询。

1 个答案:

答案 0 :(得分:0)

关键是使用models.Qexpressions.ExpressionWrapper

qs = TestModel.objects.all().annotate(
    has_user=expressions.ExpressionWrapper(
        models.Q(user_id__isnull=False),
        output_field=models.BooleanField()
    )
)