web-dev-qa-db-fra.com

django select_related - quand l'utiliser

J'essaie d'optimiser mes requêtes ORM sous Django. J'utilise connection.queries pour afficher les requêtes que Django génère pour moi.

En supposant que j'ai ces modèles:

class Book(models.Model):
 name= models.CharField(max_length=50)
 author = models.ForeignKey(Author)

class Author(models.Model):
 name = models.CharField(max_length=50)

Supposons que lorsque je génère une page Web spécifique, je souhaite afficher tous les livres, avec le nom de l'auteur à côté de chacun d'entre eux. De plus, j'affiche séparément tous les auteurs.

Alors devrais-je utiliser 

Book.objects.all().select_related("author")

Ce qui entraînera une requête JOIN. Même si je fais une ligne avant:

Author.objects.all()

Évidemment, dans le modèle, j'écrirai quelque chose comme {{book.author.name}}.
La question est donc de savoir si, lorsque j'accède à une valeur de clé étrangère (auteur), si Django a déjà cet objet d'une autre requête, cela entraînera-t-il une requête supplémentaire (pour chaque livre)? Dans ce cas, l’utilisation de select_related crée-t-elle réellement une surcharge de performances? 

16
user3599803

Django ne connaît pas d'autres requêtes! Author.objects.all() et Book.objects.all() sont des ensembles de requêtes totalement différents. Donc, si vous avez les deux dans votre vue et que vous les transmettez au contexte du modèle, mais que dans votre modèle, vous procédez comme suit:

 {% pour book in books%} 
 {{book.author.name}} 
 {% endfor%} 

et avoir N livres, cela se traduira par N requêtes supplémentaires dans la base de données (au-delà des requêtes pour obtenir tous les livres et auteurs)!

Si vous aviez plutôt fait Book.objects.all().select_related("author"), aucune requête supplémentaire ne sera effectuée dans le fragment de modèle ci-dessus.

Maintenant, select_related() ajoute bien sûr une surcharge aux requêtes. Qu'est-ce qui se passe est que lorsque vous faites un Book.objects.all() Django retournera le résultat de SELECT * FROM BOOKS. Si vous faites plutôt un Book.objects.all().select_related("author"), Django renverra le résultat de SELECT * FROM BOOKS B LEFT JOIN AUTHORS A ON B.AUTHOR_ID = A.ID. Donc, pour chaque livre, il renverra à la fois les colonnes du livre et son auteur correspondant. Cependant, cette surcharge est vraiment beaucoup plus petite comparée à la surcharge de frapper la base de données N fois (comme expliqué précédemment). 

Ainsi, même si select_related crée une légère surcharge de performances (chaque requête renvoie plusieurs champs de la base de données), il sera intéressant de l'utiliser, sauf si vous êtes absolument certain que vous aurez besoin de seulement modèle spécifique que vous interrogez.

Enfin, un bon moyen de voir vraiment combien de requêtes (et exactement) sont réellement exécutées dans votre base de données est d’utiliser Django-debug-tooblar ( https://github.com/Django-debug-toolbar/Django-debug -Barre d'outils ).

13
Serafeim
Book.objects.select_related("author")

est assez bon. Pas besoin de Author.objects.all()

{{ book.author.name }}

ne frappe pas la base de données, car book.author a déjà été pré-rempli. 

1
doniyor