Шаблон проектирования модели 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.