Reputation: 23
I have 2 models and a manytomany relationship between then, Property and Owners, they are pretty much the following:
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('Owner', on_delete=models.CASCADE)
current = models.BooleanField()
How can I annotate a new field in my Property.objects.all()
queryset with the current owner's id. (maybe using .first()
)
Upvotes: 1
Views: 41
Reputation: 476544
A simple way to do this is by making a property that will make a query (per Property
) to obtain the current owner. This thus looks like:
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()
If we however load a lot of Property
s, and we need to find the current owner for each Property
, this will result in a lot of queries.
An alternative might be to make a query to obtain the Property
s with the primary key of the current owner, then load all these owners, and finally assigning these owners to the Property
objects. This thus looks like:
We can annotate the QuerySet
with the primary key of the current owner, and then fetch these in bulk and implement the JOIN logic in 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)
After this code fragment, properties
is a list of Property
objects that have an extra attribute: .current_owner
which is an Owner
object for the person currently owning that Property
.
Upvotes: 1