Упорядочивать по подмножеству связанных полей?

У меня есть модель Release. У каждого выпуска есть свой тип, и в зависимости от этого типа различные типы Credit считаются основными. Я хочу иметь возможность упорядочивать релизы по названиям Entity их основных титров.

class Release(models.Model):
    TYPES = { "GA": "Game", "MO": "Movie", "TV": "TV Show", "MU": "Music", "BO": "Book", "BU": "Bundle" }
    TYPES_PRIMARY_ROLE = { "GA": "Developer", "MO": "Director", "TV": "Showrunner", "MU": "Primary Artist", "BO": "Author", "BU": "none" }

    type = models.CharField(choices=TYPES)

    # How to order_by the entity__names returned here?
    def get_credits_primary(self):
        return self.credits.filter(role__name=self.TYPES_PRIMARY_ROLE[self.type]).order_by("entity__name")

class Credit(models.Model):
    role = models.ForeignKey(CreditRole, on_delete=models.CASCADE, related_name="credits")
    entity = models.ForeignKey(Entity, on_delete=models.CASCADE, related_name="credits")
    release = models.ForeignKey(Release, on_delete=models.CASCADE, related_name="credits")

Я полагаю, я мог бы создать кэшированное строковое значение для основных имен пользователей, но это не похоже на хороший способ сделать это.

Если я все правильно понял, это можно сделать с помощью такого запроса:

from django.db import models

queryset = (
    Release.objects
    .alias(
        primary_role=models.Case(
            *(
                models.When(type=k, then=models.Value(v))
                for k, v in Release.TYPES_PRIMARY_ROLE.items()
            )
        ),
    )
    .alias(is_primary=models.Q(credits__role__name=models.F('primary_role')))
    .filter(is_primary=True)
    .order_by('credits__entity__name')
)

Исключите все версии, которые не являются is_primary, затем отсортируйте по “entity”.“name”. Посмотрите на исходный запрос, распечатав queryset.query, это то, что вам нужно? Вы также можете посмотреть на queryset.explain(), я не знаю, насколько это было бы эффективно при работе с большими данными.

Это решение частично адаптировано из ответа Сергея Фоменко и предназначено для PostgreSQL.

Используя StringAgg, вы можете получить список значений из связанного поля, который затем можно использовать для сортировки:

from django.contrib.postgres.aggregates import StringAgg
from django.db.models import Case, F, When

queryset = (
    Release.objects
    .alias(
        primary_role=Case(
            *(
                When(type=k, then=Value(v))
                for k, v in Release.TYPES_PRIMARY_ROLE.items()
            )
        ),
    )
    .annotate(credits_list=StringAgg(
        Case(When(credits__role__name=F("primary_role"), then="credits__entity__name")),
        delimiter=", ",
        ordering="credits__entity__name"
    ))
    .order_by("credits_list")
)
Вернуться на верх