Dans Django doc,
select_related()
"suit" les relations de clé étrangère, en sélectionnant des données supplémentaires sur les objets liés lors de l'exécution de sa requête.
prefetch_related()
effectue une recherche distincte pour chaque relation et effectue la "jointure" en Python.
Qu'est-ce que cela signifie par "faire la jointure en python"? Quelqu'un peut-il illustrer avec un exemple?
Ma compréhension est que pour une relation de clé étrangère, utilisez select_related
; et pour la relation M2M, utilisez prefetch_related
. Est-ce correct?
python
django
django-models
django-orm
NeoWang
la source
la source
Réponses:
Votre compréhension est généralement correcte. Vous utilisez
select_related
lorsque l'objet que vous allez sélectionner est un seul objet,OneToOneField
ou alors aForeignKey
. Vous utilisezprefetch_related
lorsque vous allez obtenir un "ensemble" de choses, doncManyToManyField
s comme vous l'avez dit ou inversez leForeignKey
s. Juste pour clarifier ce que je veux dire par "reverseForeignKey
s", voici un exemple:La différence est que cela
select_related
fait une jointure SQL et récupère donc les résultats dans le cadre de la table à partir du serveur SQL.prefetch_related
d'autre part exécute une autre requête et réduit donc les colonnes redondantes dans l'objet d'origine (ModelA
dans l'exemple ci-dessus). Vous pouvez utiliserprefetch_related
pour tout ce que vous pouvez utiliserselect_related
.Les compromis sont que
prefetch_related
doit créer et envoyer une liste d'ID à sélectionner à nouveau sur le serveur, cela peut prendre un certain temps. Je ne sais pas s'il y a une bonne façon de le faire dans une transaction, mais je crois comprendre que Django envoie toujours juste une liste et dit SELECT ... WHERE pk IN (..., ..., ...) fondamentalement. Dans ce cas, si les données extraites sont rares (disons les objets de l'État américain liés aux adresses des personnes), cela peut être très bon, mais si elles sont plus proches de une à une, cela peut gaspiller beaucoup de communications. En cas de doute, essayez les deux et voyez celui qui fonctionne le mieux.Tout ce qui est discuté ci-dessus concerne essentiellement les communications avec la base de données. Côté Python cependant
prefetch_related
l'avantage supplémentaire est qu'un seul objet est utilisé pour représenter chaque objet de la base de données. Avecselect_related
les objets en double seront créés en Python pour chaque objet "parent". Étant donné que les objets en Python ont un peu de surcharge de mémoire décente, cela peut également être une considération.la source
select_related
est une requête tandis queprefetch_related
deux, la première est donc plus rapide. Maisselect_related
ne vous aidera pas pourManyToManyField
- sselect_related
utilise un JOIN dans le SQL tandis queprefetch_related
la requête est exécutée sur le premier modèle, collecte tous les ID dont il a besoin pour la prélecture, puis exécute une requête avec une clause IN dans le WHERE avec tous les ID dont il a besoin. Si vous avez dit 3-5 modèles utilisant la même clé étrangère, ceselect_related
sera certainement mieux. Si vous avez des centaines ou des milliers de modèles utilisant la même clé étrangère, celaprefetch_related
pourrait être mieux. Entre les deux, vous devrez tester et voir ce qui se passe.Les deux méthodes atteignent le même objectif, pour éviter les requêtes db inutiles. Mais ils utilisent différentes approches pour l'efficacité.
La seule raison d'utiliser l'une de ces méthodes est lorsqu'une seule grande requête est préférable à de nombreuses petites requêtes. Django utilise la grande requête pour créer des modèles en mémoire de manière préventive plutôt que d'effectuer des requêtes à la demande sur la base de données.
select_related
effectue une jointure à chaque recherche, mais étend la sélection pour inclure les colonnes de toutes les tables jointes. Cependant, cette approche comporte une mise en garde.Les jointures ont le potentiel de multiplier le nombre de lignes dans une requête. Lorsque vous effectuez une jointure sur une clé étrangère ou un champ un-à-un, le nombre de lignes n'augmente pas. Toutefois, les jointures plusieurs à plusieurs n'ont pas cette garantie. Donc, Django restreint
select_related
aux relations qui ne se traduiront pas de manière inattendue par une adhésion massive.Le "join in python" pour
prefetch_related
est un peu plus alarmant qu'il ne devrait l'être. Il crée une requête distincte pour chaque table à joindre. Il filtre chacune de ces tables avec une clause WHERE IN, comme:Plutôt que d'effectuer une jointure unique avec potentiellement trop de lignes, chaque table est divisée en une requête distincte.
la source
Comme le dit la documentation de Django:
Plus d'informations à ce sujet: https://docs.djangoproject.com/en/2.2/ref/models/querysets/#prefetch-related
la source
Passé en revue les réponses déjà publiées. Je pensais que ce serait mieux si j'ajoute une réponse avec un exemple réel.
Disons que vous avez 3 modèles Django qui sont liés.
Ici, vous pouvez interroger le
M2
modèle et sesM1
objets relatifs en utilisant leselect_relation
champ et lesM3
objets en utilisantprefetch_relation
champ.Cependant , comme nous l' avons mentionné
M1
la relation de » deM2
estForeignKey
, elle retourne juste seulement 1 record pour toutM2
objet. La même chose s'applique pourOneToOneField
également.Mais
M3
la relation deM2
est unManyToManyField
qui pourrait renvoyer un certain nombre deM1
objets.Prenons un cas où vous avez 2
M2
objetsm21
,m22
qui ont les mêmes 5M3
objets associés avec des ID1,2,3,4,5
. Lorsque vous récupérez desM3
objets associés pour chacun de cesM2
objets, si vous utilisez select related, voici comment cela va fonctionner.Pas:
m21
objet.M3
objets liés à l'm21
objet dont les ID sont1,2,3,4,5
.m22
objet et tous les autresM2
objets.Comme nous avons les mêmes
1,2,3,4,5
identifiants pour les deuxm21
,m22
objets, si nous utilisons l'option select_related, il va interroger la base de données deux fois pour les mêmes ID qui ont déjà été récupérés.Au lieu de cela, si vous utilisez prefetch_related, lorsque vous essayez d'obtenir des
M2
objets, il prendra note de tous les ID que vos objets ont renvoyés (Remarque: seuls les ID) lors de l'interrogation de laM2
table et, comme dernière étape, Django va effectuer une requête vers laM3
table avec l'ensemble de tous les ID que vosM2
objets ont renvoyés. et rejoignez-les pourM2
objets utilisant Python au lieu de la base de données.De cette façon, vous interrogez tous les
M3
objets une seule fois, ce qui améliore les performances.la source