2009-05-13 10 views
8

Dans Django, j'ai deux modèles:Comment faire cette requête de jointure dans Django

class Product(models.Model): 
    name = models.CharField(max_length = 50) 
    categories = models.ManyToManyField(Category) 

class ProductRank(models.Model): 
    product = models.ForeignKey(Product) 
    rank = models.IntegerField(default = 0) 

je mets le rang dans une table séparée parce que chaque affichage d'une page entraînera le rang de changer et j'étais inquiet que toutes ces écritures feraient ralentir mes autres requêtes (surtout lues).

: Je réunirai une liste de Products d'une simple requête:

cat = Category.objects.get(pk = 1) 
products = Product.objects.filter(categories = cat) 

Je voudrais maintenant obtenir tous les rangs pour ces produits. Je préférerais tout faire en une fois (en utilisant une jointure SQL) et je me demandais comment exprimer cela en utilisant le mécanisme de requête de Django.

Quelle est la bonne façon de le faire dans Django?

+0

Cela ressemble à une optimisation prématurée. Avez-vous des preuves de profileur que "toutes ces écritures ralentiraient mes autres requêtes"? Ou que cet effet est pire que le ralentissement des JOINs supplémentaires quand vous avez besoin de lire le rang? Écrivez-le d'abord simplement et n'optimisez pas avant d'avoir démontré que c'est nécessaire. –

Répondre

12

Cela peut être fait dans Django, mais vous aurez besoin de restructurer vos modèles un peu différemment:

class Product(models.Model): 
    name = models.CharField(max_length=50) 
    product_rank = models.OneToOneField('ProductRank') 

class ProductRank(models.Model): 
    rank = models.IntegerField(default=0) 

Maintenant, lors de la récupération des objets produits, vous pouvez la suite de l'un à une relation dans un requête en utilisant la méthode select_related():

Product.objects.filter([...]).select_related() 

Cela produira une requête qui va chercher le produit rangs en utilisant une jointure:

SELECT "example_product"."id", "example_product"."name", "example_product"."product_rank_id", "example_productrank"."id", "example_productrank"."rank" FROM "example_product" INNER JOIN "example_productrank" ON ("example_product"."product_rank_id" = "example_productrank"."id") 

J'ai dû déplacer le champ de relation entre Product et ProductRank vers le modèle Product car il semble que select_related() suit les clés étrangères dans un seul sens.

+0

Haha. Je lisais les docs sur OneToOneField aujourd'hui et je me demandais à quoi ça servait! Merci pour l'excellente info. –

2

Ajoutez un appel à la méthode select_related() du QuerySet, même si je ne suis pas sûr que les références soient saisies dans les deux directions, c'est la réponse la plus probable.

4

Je n'ai pas vérifié, mais:

products = Product.objects.filter(categories__pk=1).select_related() 

devez saisir chaque instance.

Questions connexes