Аннотируйте модели кверисетов полями из "сквозной" модели
Кажется, что это должно быть просто, но я не смог найти ответ, несмотря на множество гуглений.
У меня есть две модели, объединенные отношением "многие ко многим" через третью модель, которая имеет некоторые дополнительные поля.
class User(AbstractUser):
pass
class Project(models.Model):
name = models.CharField(max_length=100, unique=True, blank=False)
description = models.CharField(max_length=255, blank=True)
users = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='projects', through='UserProject')
# additional project fields
class UserProject(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
favorite = models.BooleanField(default=False)
last_used = models.DateTimeField(default=timezone.now)
Я хочу получить доступ к проектам данного пользователя вместе с полями favorite
и last_used
для этого пользователя для каждого проекта.
Я не могу понять, как это сделать, используя аннотации или любой другой метод при вызове user.projects
. Если я вызываю сквозное отношение напрямую (т.е. user.userproject_set
), я могу добавить поля из проекта одним sql-запросом, вызвав, например, user.userproject_set.select_related('project').annotate(name='project.name')
, но тогда модели в результирующем наборе запросов будут UserProjects, а не Projects, что не то, что я хочу.
Написать sql-запрос для выполнения того, что я хочу, было бы тривиально (в основном это был бы тот же запрос, который Django создает для присоединения полей Project к кверисету UserProject). Есть ли способ заставить Django добавить поля из сквозной таблицы к моделям в кверисете, доступ к которым осуществляется через одну сторону отношения "многие-ко-многим"?
Просто используйте связанное имя для доступа к связанным объектам. Используя это, вы должны получить Project
объектов:
user.projects.all()
После перебора множества различных вариантов, включая FilterRelation, я нашел способ получить желаемое, используя метод QuerySet extra
для добавления нужных мне полей из промежуточной таблицы:
user.projects.extra(select={
'favorite': 'app_userproject.favorite',
'last_used': 'app_userproject.last_used'
})
Не требуется никаких дополнительных SQL-запросов/заходов в базу данных, никаких новых объединений и никаких изменений в предложении where
, потому что соответствующие объединения и условия уже являются частью запроса, который Django строит для получения дочерних моделей из отношения "многие-ко-многим". Именно поэтому кажется, что в Django должен быть какой-то способ сделать это, не прибегая к extra
- который Django грозится обесценить.