Шаблон проектирования модели Django - получение самого большого связанного объекта

Django позволяет отслеживать отношения link, но ни один из методов фильтрации не позволяет вам получить младших/старших или макс/мин, afaik.

В Laravel есть " имеет один из многих", и я хотел бы, чтобы в Django было что-то подобное, не требуя оконных функций, которые имеют свои собственные ограничения.

Аннотации, несмотря на свою перспективность, также являются тупиком.

Поэтому мне интересно, какова наилучшая конструкция для следующей ситуации:

У меня есть "модель A", экземпляры которой будут проходить через несколько статусов в течение своего жизненного цикла ("создан", "обработка", "завершен"). Я хочу знать, какой статус имеет экземпляр модели A в данный момент, а также иметь запись о том, когда каждый статус был в силе. Я не хочу разбирать журналы

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

Однако если я хочу получить текущее состояние (экземпляр модели B) для всех экземпляров модели A, мне нужно сделать n+1 запрос к базе данных. Это кажется неоптимальным.

Какие есть альтернативы?

Однако если я хочу получить текущее состояние (экземпляр модели B) для всех экземпляров модели A, мне нужно сделать n+1 запрос к базе данных. Это кажется неоптимальным.

Нет, вы можете использовать Subquery выражения [Django-doc]. Действительно, если у вас есть две модели:

class Item(models.Model):
    name = models.CharField(max_length=128)


class ItemStatus(models.Model):
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    status = models.CharField(max_length=128)
    started = models.DateTimeField(auto_now_add=True)

Вы можете аннотировать каждый Item последним статусом с помощью:

from django.db.models import OuterRef, Subquery

Item.objects.annotate(
    last_status=Subquery(
        ItemStatus.objects.filter(
            item_id=OuterRef('pk')
        ).order_by('-started').values('status')[:1]
    )
)

Для каждого Item будет дополнительный атрибут .last_status, который будет содержать .status связанного ItemStatus, который начался последним. Если такого StatusItem нет, last_status будет None (NULL).

Это будет определяться подзапросами на стороне базы данных, следовательно, это делается в том же запросе, в котором вы извлекаете Item, и поэтому не страдает от проблемы N+1.

Вернуться на верх