Django получает значение максимальной длины из одной модели с помощью Coalesce

Я пытаюсь написать следующий необработанный запрос с помощью ORM. Я не уверен, возможно ли это или нет.

select first_name,
       middle_name,
       COALESCE(middle_name, (
           select middle_name
           from contacts c2
           where c2.first_name = c1.first_name
             and c2.last_name = c1.last_name
             and c2.middle_name is not null
           order by length(c2.middle_name) desc
           limit 1
       )
           ) expected,
       last_name
from contacts c1

Ожидаемый результат будет следующим, если middle_name равно null, получите среднее имя из другой записи, которая имеет те же first_name и last_name.

id| first_name | middle_name | expected | last_name
1 | ahmet      |   <NULL>    |  burak   |   ozyurt
2 | ahmet      |   burak     |  burak   |   ozyurt

DB: Postgres
Django Версия: 3.12

Используя django ORM, вы можете выполнить тот же запрос с помощью следующего кода

from django.db import models
from django.db.models.functions import Coalesce, Length

matched_middle_name_queryset = Contact.objects.filter(
    first_name=models.OuterRef("first_name"),
    last_name=models.OuterRef("last_name"),
    middle_name__isnull=False,
).annotate(
    middle_name_len=Length("middle_name")
).order_by("-middle_name_len").values("middle_name")[:1]

result = Contact.objects.annotate(
    matched_middle_name=models.Subquery(matched_middle_name_queryset)
    expected=Coalesce(
        models.F("middle_name")
        models.F("matched_middle_name"),
    ).values("id", "first_name", "middle_name", "expected", "last_name")
)

Пояснения

  1. models.OuterRef используется для ссылки на поле из родительского запроса подзапроса.
  2. - префикс в order_by("-middle_name_len") используется для нисходящего порядка
  3. .
  4. .values("middle_name") предназначен для выбора только middle_name значений.
  5. нарезка [:1] предназначена для ограничения результата подзапроса одним значением.

Советы

  • Вы можете использовать result.query, чтобы проверить, какой запрос ORM будет генерировать для вас.
Вернуться на верх