2009-12-05 6 views
15

Cela ne semble pas fonctionner dans django 1.1 (je crois que cela nécessitera une sous-requête est donc le titre)Django annoter requête ensemble avec un compte sur sous_requête

qs.annotate(interest_level= \ 
      Count(Q(tags__favoritedtag_set__user=request.user)) 
      ) 

Il y a des éléments dans mon jeu de requête qui sont marqués et les tags peuvent être favorisés par les utilisateurs, je voudrais calculer combien de fois un utilisateur a favorisé chaque élément de l'ensemble via des balises.

est-il un moyen de construire une requête comme cela sans l'aide supplémentaire()?

Merci.

+0

-vous obtenez un message d'erreur? Pourriez-vous fournir les modèles avec lesquels vous travaillez? – cethegeek

+0

l'erreur est "exception 'Q' objet n'a pas d'attribut 'split'", geradeausanwalt est droit fonctions d'agrégation ne prennent pas les objets Q comme arguments. Les modèles dans sa réponse sont similaires au mien. – Evgeny

Répondre

10

En regardant la fonction add_aggregate dans les django/db/models/sql/query.py, objets de requête ne seront pas acceptées comme valeurs d'entrée.

Malheureusement, il n'y a actuellement aucun moyen direct au sein de Django pour agréger/annoter sur ce qui équivaut à un queryset, surtout pas celui qui est en outre filtré en quelque sorte.

En supposant que les modèles suivants:

class Item(models.Model): 
    name = models.CharField(max_length=32) 

class Tag(models.Model): 
    itemfk = models.ForeignKey(Item, related_name='tags') 
    name = models.CharField(max_length=32) 

class FavoritedTag(models.Model): 
    user = models.ForeignKey(User) 
    tag = models.ForeignKey(Tag) 

De plus, vous ne pouvez pas annoter un queryset sur les champs définis par .extra().

On pourrait tomber dans SQL views.py comme ceci:

from testing.models import Item, Tag, FavoritedTag 
from django.shortcuts import render_to_response 
from django.contrib.auth.decorators import login_required 
from django.utils.datastructures import SortedDict 

@login_required 
def interest_level(request): 
    ruid = request.user.id 

    qs = Item.objects.extra(
     select = SortedDict([ 
      ('interest_level', 'SELECT COUNT(*) FROM testing_favoritedtag, testing_tag \ 
      WHERE testing_favoritedtag.user_id = %s \ 
      AND testing_favoritedtag.tag_id = testing_tag.id \ 
      AND testing_tag.itemfk_id = testing_item.id'), 
     ]), 
     select_params = (str(ruid),) 
    ) 

    return render_to_response('testing/interest_level.html', {'qs': qs}) 

Modèle:

{% for item in qs %} 
    name: {{ item.name }}, level: {{ item.interest_level }}<br> 
{% endfor %} 

J'ai testé cela en utilisant MySQL5. Puisque je ne suis pas un expert SQL, je serais curieux de savoir comment optimiser ici, ou s'il y a une autre façon de "réduire" la quantité de SQL. Peut-être qu'il existe un moyen intéressant d'utiliser la fonctionnalité ici directement dans SQL?

1

Si vous voulez éviter de faire tomber à SQL brute, une autre façon à la peau ce chat serait d'utiliser une méthode de modèle, ce qui vous donnera un nouvel attribut sur le modèle à utiliser dans vos modèles. Non testé, mais quelque chose comme ceci sur votre modèle Balises devrait fonctionner:

class Tag(models.Model): 
    itemfk = models.ForeignKey(Item, related_name='tags') 
    name = models.CharField(max_length=32) 

    def get_favetag_count(self): 
     """ 
     Calculate the number of times the current user has favorited a particular tag 
     """ 

     favetag_count = FavoritedTag.objects.filter(tag=self,user=request.user).count() 
     return favetag_count 

Ensuite, dans votre modèle, vous pouvez utiliser quelque chose comme:

{{tag}} ({{tag.get_favetag_count}}) 

L'inconvénient de cette approche est qu'elle pourrait frapper la base de données plus si vous êtes dans une grande boucle ou quelque chose. Mais en général cela fonctionne bien et contourne l'incapacité d'annoter pour faire des requêtes sur des modèles liés. Et évite d'avoir à utiliser du SQL brut.

+5

Pas du tout équivalent aux annotations, c'est terriblement inefficace. – hcalves

Questions connexes