2010-12-04 2 views
0

J'essaie de réduire mon nombre de requêtes et j'ai une question.Django - Scénario courant: Produit avec M2M -> Catégories. Comment interroger efficacement toutes les catégories d'un ensemble de requêtes?

J'ai une configuration de modèle de produit typique qui peut avoir plusieurs catégories.

Product M2M -> Category

J'ai une fonction définie qui tire la catégorie principale (laisse la parole juste son la plus récente):

class Category(models.Model): 
    name = models.CharField(max_length=1) 

class Product(models.Model): 
    category = models.ManyToManyField(Category) 

    def main_category(self): 
     return self.category.latest('id') 
     # or any other way to determine main category 

J'itérer produits dans mon modèle, affichant sa catégorie principale.

{% for product in products %} 
    Name: {{ product.name }} 
    Category: {{ product.main_category }} 
{% endfor %} 

Cela provoque une requête pour chaque produit. Comment puis-je obtenir tout en moins de requêtes, en Python?

Pour mes autres modèles MultipleObjectFK -> Product j'ai pu partition my queries (slideshow) et utiliser seulement 2 requêtes + python, mais je ne peux pas sembler l'appliquer à m2m parce que quand je tire les catégories qui sont référencés dans mon queryset produit, Je n'ai aucune idée de quel produit a déclenché la correspondance de catégorie.

Je m'appuie sur la configuration d'un champ main_category sur mon modèle qui a un ID de catégorie calculé chaque fois qu'un signal M2MField est modifié.

Merci pour votre temps:)

Répondre

2

Voici ce que je ferais. Créez un modèle through pour la relation ManyToMany et collez un champ booléen sur ce modèle pour indiquer l'état main_category. Ensuite, vous pouvez simplement interroger ce modèle, en utilisant select_related() afin qu'il suive automatiquement les deux clés étrangères - et maintenant vous pouvez parcourir les objets through et obtenir à la fois Product et Category sans aucune requête supplémentaire.

class Product(models.Model): 
    category = models.ManyToManyField(Category, through="ProductCategory") 

class ProductCategory(models.Model): 
    product = models.ForeignKey(Product) 
    category = models.ForeignKey(Category) 
    main = models.BooleanField(default=False) 

Vue:

prod_cats = ProductCategory.objects.filter(main=True).select_related() 
modèle

:

{% for prod_cat in prod_cats %} 
    Name: {{ prod_cat.product.name }} 
    Category: {{ prod_cat.category.name }} 
{% endfor %} 
+0

utilisation vraiment intéressante du "à" modèle, je me souviendrai de cette pour la prochaine fois. Merci! –

+0

Merci pour cette idée! Il * se sent * étrange, mais fonctionne. Tous les produits doivent avoir une catégorie, donc je peux tout réécrire en utilisant ProductCategory au lieu de Product. Ou réduisez au moins à 2 requêtes sans aucun changement ailleurs en faisant ProductCategory.filter (main = True, pk__in = productqueryset_ids) .select_related() et en ajoutant un attribut main_category au jeu de requête d'origine du produit. –

Questions connexes