Как преобразовать поиск в выражение для использования в `QuerySet.annotate`? - PullRequest
0 голосов
/ 08 января 2019

Тема может быть немного неясной, поэтому вот пример. Допустим, у меня есть модель:

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

Мне нужен QuerySet, содержащий поле has_user, которое в основном соответствовало бы следующему SQL-запросу

select *, user_id is not null as has_user
from app_testmodel

Как я могу объяснить это has_user поле Django при использовании QuerySet.annotate?


Мне известно, что у Джанго есть такие понятия, как models.F, models.Q, models.lookups, models.expressions и т. Д., Но я не могу понять, как применять их в моем случае. Насколько мне известно, это вопрос преобразования слова «поиск» в логическое «выражение», где

  • «Поиск» - это что-то вроде 'user_id__isnull' или lookups.IsNull(models.F('user_id')) на языке Django ORM.

  • «Выражение» - это что-то вроде 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 голосов
/ 09 января 2019

Ключ должен использовать models.Q и expressions.ExpressionWrapper:

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