I have a system where there is a many to one relationship with a number a model (say 1 a -> many b) and that many model has a one to one relationship with another model (say 1 b -> 1 c). Drawn like so:
/--- b1 --- c1
/
a ---- b2 --- c2
\
\--- b3 --- c3
I'm determined to create a method in that collects all the c
's that correspond to a
.
Given an model system with the same structure to mine, the best I could come up with is shown in the method: Person.find_important_treats()
.
Is there a better way that does not involve so many calls to the database?
from django.db import models
class Person(models.Model):
""" The 'a' from my above example """
def find_important_treats(self):
return (pet.treat for pet in self.pets)
class Pet(models.Model):
""" The 'b' from my above example """
owner = models.ForeignKey(
to=Person,
related_name='pets'
)
favourite_treat = models.ForeignKey(
to=Treat,
)
class Treat(models.Model):
""" The 'c' from my above example """
pass
I suggest the two almost similar solution depending on your use case:
class Person(models.Model):
""" The 'a' from my above example """
@property
def iter_important_treats(self):
return (pet.treat_id for pet in self.pets.all()) # will use the cached objects if they exist
person = Person.objects.get(id=person_id).select_related('pets') # to cache the pets list within the person object avoiding future additional queries
treats = Treat.objects.filter(id__in=person.iter_importent_treats)
class Person(models.Model):
""" The 'a' from my above example """
@property
def iter_important_treats(self):
return iter(self.pets.values_list('treat_id', flat=True)) # caching will not affect the query behviour
person = Person.objects.get(id=person_id)
treats = Treat.objects.filter(id__in=person.iter_importent_treats)
NB:
treat_id
instead treat__id
to avoid additional join queries, because the django saves already the treat_id
at the Pet object level but if you use treat__id
then you force a join query.The following should do what you are after:
def find_important_treats(self):
return Treat.objects.filter(id__in=person.pets.values_list('treat_id'))
It obtains all of the ids
of the Treat
s that the pets have and then returns them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With