web-dev-qa-db-fra.com

prefetch_related pour plusieurs niveaux

Si mes modèles ressemblent à:

class Publisher(models.Model):
    pass

class Book(models.Model):
    publisher = models.ForeignKey(Publisher)

class Page(models.Model):
    book = models.ForeignKey(Book)

et je voudrais obtenir le jeu de requêtes pour Publisher je fais Publisher.object.all(). Si je veux m'assurer de faire une prélecture, je peux faire:

Publisher.objects.all().prefetch_related('book_set')`

Mes questions sont:

  1. Existe-t-il un moyen d'effectuer cette prélecture à l'aide de select_related Ou dois-je utiliser prefetch_related?
  2. Existe-t-il un moyen de pré-extraire le page_set? Cela ne fonctionne pas:

Publisher.objects.all().prefetch_related('book_set', 'book_set_page_set')

33
Alex Rothberg
  1. Non, vous ne pouvez pas utiliser select_related Pour une relation inverse. select_related Fait une jointure SQL, donc un seul enregistrement du jeu de requêtes principal doit en référencer exactement un dans la table associée (champs ForeignKey ou OneToOne). prefetch_related Fait en fait une deuxième requête totalement distincte, met les résultats en cache, puis "les joint" dans le jeu de requêtes en python. Il est donc nécessaire pour les champs ManyToMany ou inverses ForeignKey.

  2. Avez-vous essayé deux traits de soulignement pour effectuer les pré-prélèvements à plusieurs niveaux? Comme ceci: Publisher.objects.all().prefetch_related('book_set', 'book_set__page_set')

31
jproffitt

Depuis Django 1.7, les instances de Django.db.models.Prefetch la classe peut être utilisée comme argument de .prefetch_related. Prefetch le constructeur d'objet a un argument queryset qui permet de spécifier des prélèvements imbriqués sur plusieurs niveaux comme celui-ci:

Project.objects.filter(
        is_main_section=True
    ).select_related(
        'project_group'
    ).prefetch_related(
        Prefetch(
            'project_group__project_set',
            queryset=Project.objects.prefetch_related(
                Prefetch(
                    'projectmember_set',
                    to_attr='projectmember_list'
                )
            ),
            to_attr='project_list'
        )
    )

Il est stocké dans des attributs avec _list suffixe car j'utilise ListQuerySet pour traiter les résultats de la prélecture (filtre/ordre).

21
Dmitriy Sintsov