2010-11-24 13 views
0

Hay, j'ai un modèle simple comme celui-ciCommande d'une méthode

def Article(models.Model): 
    upvotes = models.ManyToManyField(User, related_name='article_upvotes') 
    downvotes = models.ManyToManyField(User, related_name='article_downvotes') 

    def votes(self): 
     return self.upvotes - self.downvotes 

Avec la vue que je peux faire des choses comme

article_votes = article.votes 

Suis-je capable de commander par la fonction votes? Quelque chose comme

article = Article.objects.order_by('votes') 

EDIT

Je ne suis pas près de mon système dev pour le moment, de sorte que la syntaxe est peut-être un peu hors.

+0

Je suis à peu près sûr que le code est faux, puisque cette méthode n'a aucun sens. –

+0

Voir la modification :) – dotty

+0

"' article_votes = article.votes' "est une affectation de fonction, pas une assignation de nombre, et la fonction' votes() 'soulève probablement une exception - Je ne pense pas que la soustraction des gestionnaires de modèles soit un opération valide. –

Répondre

2

Vous pouvez trier la liste après la requête renvoie les résultats:

article_votes = sorted(article.votes, key=lambda a: a.votes()) 

sorted prend une liste et le trie. Vous pouvez fournir une fonction personnalisée qui prend un élément et renvoie la valeur à utiliser lors de la comparaison d'éléments. lambda a: a.votes() est une fonction anonyme qui prend un article et renvoie le nombre de votes sur l'article.

Si vous voulez récupérer tous les articles, il n'y a aucun inconvénient à cette solution. D'un autre côté, si vous ne voulez que les 10 premiers articles par votes, alors vous tirez tous les articles de la base de données au lieu de laisser le db faire le tri, et de ne rendre que les dix premiers. Par rapport à une solution SQL pure, cela récupère beaucoup plus de données de la base de données.

+0

Désolé, je ne comprends pas ce que vous dites dans la deuxième phrase. Aussi, pourriez-vous expliquer la clé = lambda a: a.votes() partie de votre exemple s'il vous plaît. Merci. – dotty

+0

Donc, il est plus facile de saisir tous les enregistrements, puis de trier, plutôt que d'obtenir SQL faire le tri? Merci pour l'explication de ce que fait lambda, ad argument qu'il prend. – dotty

0

Ceci est une version plus rapide de ce que Ned Batchelder a suggéré - comme il le fait le décompte dans la base de données:

articles = list(Article.objects.annotate(upvote_num=models.Count('upvotes'), downvote_num=models.Count('downvotes'))) 
articles.sort(key=lambda a: a.upvotes - a.downvotes) 

Vous pouvez aussi le faire complètement à l'intérieur de la base de données:

articles = Article.objects.raw(""" 
    SELECT DISTINCT id from articles_article, 
    COUNT(DISTINCT upv) AS num_upvotes, 
    COUNT(DISTINCT downv) AS num_downvotes, 
    (num_upvotes - num_downvotes) AS score 

    INNER JOIN [article_user_upvotes_m2m_table_name] AS upv 
    ON upv.article_id = id 

    INNER JOIN [article_user_downvotes_m2m_table_name] AS downv 
    ON downv.article_id = id 

    GROUP BY id 
    ORDER BY score 
""") 

- mais je ne suis pas sûr que la double jointure soit une bonne idée dans votre cas. En outre, je ne suis pas sûr si tous ces DISCTINCTs sont nécessaires. Il est très probable que cette requête puisse être réécrite d'une certaine manière, mais je n'ai pas d'idée pour le moment.

+0

Vous recherchez '.extra()'. –