25

Ich habe zwei Modelle in Django. Der erste hat die Hierarchie dessen, welche Jobfunktionen (Positionen) zu welchen anderen Positionen berichten, und der zweite ist die Person und welche Jobfunktion sie haben.select_related mit umgekehrten Fremdschlüssel

class PositionHierarchy(model.Model): 
    pcn = models.CharField(max_length=50) 
    title = models.CharField(max_length=100) 
    level = models.CharField(max_length=25) 
    report_to = models.ForeignKey('PositionHierachy', null=True) 


class Person(model.Model): 
    first_name = models.CharField(max_length=50) 
    last_name = models.CharField(max_length=50) 
    ... 
    position = models.ForeignKey(PositionHierarchy) 

Wenn ich eine Person aufnehmen müssen, und ich möchte die Person, die Manager zu finden, habe ich

manager = person.position.report_to.person_set.all()[0] 
# Can't use .first() because we haven't upgraded to 1.6 yet 

zu tun Wenn ich Menschen mit einem QuerySet bekommen, kann ich mitmachen (und vermeiden ein zweite Reise in die Datenbank) mit Position und report_to mit Person.objects.select_related('position', 'position__reports_to').filter(...), aber gibt es eine Möglichkeit, eine weitere Reise in die Datenbank zu vermeiden, um die person_set zu bekommen? Ich habe versucht, 'position__reports_to__person_set' oder nur position__reports_to__person zu der select_related hinzuzufügen, aber das scheint die Abfrage nicht zu ändern. Ist das was prefetch_related ist?

Ich möchte einen benutzerdefinierten Manager, so dass, wenn ich eine Abfrage, um Personendatensätze zu bekommen, ich auch ihre PositionHeirarchie und ihre Person Person Datensatz ohne mehr Rundreisen in die Datenbank. Das ist, was ich habe, so weit:

class PersonWithManagerManager(models.Manager): 
    def get_query_set(self): 
     qs = super(PersonWithManagerManager, self).get_query_set() 
     return qs.select_related(
      'position', 
      'position__reports_to', 
     ).prefetch_related(
     ) 
+0

wahrscheinlich ein Tippfehler, aber es sollte 'get_queryset()', 'nicht get_query_set' sein. – Paolo

+1

@Paolo es war 'get_query_set' in Django 1.5. –

Antwort

25

Ja, das ist es, was prefetch_related() ist. Es erfordert eine zusätzliche Abfrage, aber die Idee ist, dass es alle zugehörigen Informationen auf einmal erhält, anstatt einmal pro Person.

In Ihrem Fall:

qs.select_related('position__report_to') 
    .prefetch_related('position__report_to__person_set') 

sollte zwei Abfragen benötigen, unabhängig von der Anzahl der Persons in dem ursprünglichen Abfrage-Set.

Vergleichen Sie dieses Beispiel aus dem documentation:

>>> Restaurant.objects.select_related('best_pizza') 
         .prefetch_related('best_pizza__toppings')