Django cannot define a one-to-many set of requests for a one-to-one relationship

I have a system in which there are many relationships with the model number (say 1 a → many b), and that many models have a one-to-one relationship with another model (say 1 b → 1 c). Drawn like this:

/--- b1 --- c1 / a ---- b2 --- c2 \ \--- b3 --- c3 

I decided to create a method that collects all c that match a .

Given a model system with the same structure that I could find, it is shown in the method: Person.find_important_treats() .

Is there a better way that doesn't include 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 
+5
source share
2 answers

I suggest two almost similar solutions depending on your use case:

  • Using caching
  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) 
  • Without using caching:
 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) 

Note:

  • we use treat_id instead of treat__id to avoid additional join requests, since django already saves treat_id at the Pet object level, but if you use treat__id , then you force a join request.
  • Constraining a property with an identifier iterator is just for reversibility and ease of maintenance
+4
source

The following should do what you need:

 def find_important_treats(self): return Treat.objects.filter(id__in=person.pets.values_list('treat_id')) 

Gets all the ids of Treat that animals have, and then returns them.

+2
source

Source: https://habr.com/ru/post/1274055/


All Articles