如何将查找转换为表达式以在“QuerySet.annotate”中使用?

How to convert a lookup to an expression to use in `QuerySet.annotate`?

题目可能有点晦涩难懂,举个例子。假设我有一个模型:

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

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

select *, user_id is not null as has_user
from app_testmodel

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


我知道 Django 有 models.Fmodels.Qmodels.lookupsmodels.expressions 等概念,但我无法理解如何将它们应用到我的情况。据我所知,这是将 "lookup" 转换为布尔值 "expression" 的问题,其中

到目前为止,我只设法将 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()))

但这是一个尴尬的解决方法。必须有更好的解决方案:更合适、更简单、更清洁 w.r.t。 Python 代码和生成的 SQL 查询。

关键是使用models.Qexpressions.ExpressionWrapper:

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