web-dev-qa-db-fra.com

select_related avec inverser les clés étrangères

J'ai deux modèles dans Django. Le premier a la hiérarchie de quelles fonctions (postes) rapportent à quels autres postes, et le second est les personnes et la fonction qu'ils occupent.

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)

Lorsque j'ai un dossier Personne et que je veux trouver le responsable de la personne, je dois faire

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

Si je reçois des personnes avec un QuerySet, je peux rejoindre (et éviter un deuxième voyage dans la base de données) avec position et report_to en utilisant Person.objects.select_related('position', 'position__reports_to').filter(...), mais y a-t-il un moyen d'éviter d'en créer un autre voyage à la base de données pour obtenir le person_set? J'ai essayé d'ajouter 'position__reports_to__person_set' Ou simplement position__reports_to__person À select_related, Mais cela ne semble pas changer la requête. Est-ce à cela que sert prefetch_related?

J'aimerais créer un gestionnaire personnalisé pour que lorsque je fais une requête pour obtenir des enregistrements de personne, j'obtienne également leur PositionHeirarchy et l'enregistrement de personne de leur gestionnaire sans aller-retour supplémentaire dans la base de données. Voici ce que j'ai jusqu'à présent:

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(
        )
30
Paul Tomblin

Oui, c'est à cela que sert prefetch_related(). Il nécessitera une requête supplémentaire, mais l'idée est qu'il obtiendra toutes les informations associées en une seule fois, au lieu d'une fois par Person.

Dans ton cas:

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

doit nécessiter deux requêtes, quel que soit le nombre de Persons dans l'ensemble de requêtes d'origine.

Comparez cet exemple de la documentation :

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