Как аннотировать поле информацией из конкретной многотомной сущности?

У меня есть 2 модели и множество отношений между ними, Property и Owners, они примерно следующие:

class Property(models.Model):
    name = models.CharField(max_length=50)
    owners = models.ManyToManyField('Owner', through='PropertyOwner')
    ...

class Owner(models.Model):
    name = models.CharField("Owner's name", max_length=50)
    ...

class PropertyOwner(models.Model):
    property = models.ForeignKey('Property', on_delete=models.CASCADE)
    owner = models.ForeignKey('Property', on_delete=models.CASCADE)
    current = models.BooleanField()

Как я могу аннотировать новое поле в моем Property.objects.all() queryset с идентификатором текущего владельца. (возможно, используя .first())

Простой способ сделать это - создать свойство, которое будет делать запрос (по Property) для получения текущего владельца. Таким образом, это выглядит следующим образом:

class Property(models.Model):
    name = models.CharField(max_length=50)
    owners = models.ManyToManyField('Owner', through='PropertyOwner')

    @property
    def current_owner(self):
        return self.owners.filter(propertyowner__current=True).first()

Однако если мы загрузим много Property, и нам нужно будет найти текущего владельца для каждого Property, это приведет к большому количеству запросов.

Альтернативой может быть запрос для получения Property с первичным ключом текущего владельца, затем загрузка всех этих владельцев, и, наконец, присвоение этих владельцев объектам Property. Таким образом, это выглядит следующим образом:

Мы можем аннотировать QuerySet первичным ключом текущего владельца, а затем получить их оптом и реализовать логику JOIN в Django:

from operator import attrgetter

properties = list(Property.objects.filter(
    propertyowner__current=True
).annotate(
    current_owner_id=F('owners__pk')
))

owners = set(map(attrgetter('current_owner_id'), properties))
owners = {
    owner.pk: owner
    for owner in Owner.objects.filter(pk__in=owners)
}

for property in properties:
    property.current_owner = owners.get(property.current_owner_id)

После этого фрагмента кода properties представляет собой список Property объектов, имеющих дополнительный атрибут: .current_owner который является Owner объектом для человека, который в настоящее время владеет этим Property.

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