2009-02-16 11 views
5

J'utilise le bit de code suivant dans mon application Django:NULL Traiter comme '0' dans le modèle Django

pictures = gallery.picture_set.annotate(score=models.Sum('picturevote__value')).order_by('-score') 

Il y a une table de galeries. Dans chacun d'eux sont des images. Lorsqu'un utilisateur vote en haut ou en bas d'une image, une nouvelle ligne dans «picturevote» est insérée et connectée à l'image. Ensuite, je peux obtenir le score total pour les images. Maintenant, je veux commander les photos d'une galerie par leurs valeurs de score. Mais en raison des jointures de la table, il peut y avoir la valeur NULL pour le score quand il n'y a pas de votes du tout. Néanmoins, un score de 'NULL' doit être traité comme '0'.

Des idées?

modifier: Ok, voici quelques explications supplémentaires: Le problème est que l'agrégation dans l'exemple ci-dessus définit score à NULL. Quand je veux afficher le score que j'utilise quelque chose comme ceci:

score = self.picturevote_set.aggregate(models.Sum('value'))[ 'value__sum' ] or 0 

Ensuite, l'agrégation conduit soit à NULL (s'il n'y a pas de lignes picturevote) ou une certaine valeur. Si elle est NULL, l'expression-or la convertit en une valeur entière affichable. Mais cela ne résout que les problèmes d'affichage causés par la valeur NULL. Lorsque je veux trier les images par cette valeur score comme dans le premier exemple de code, toutes les entrées avec NULL sont placées à la fin du jeu de résultats ordonné. D'abord il y a des images avec des scores positifs, puis il y a les images avec des valeurs négatives et ALORS il y a les images qui n'ont pas été votées vers le haut ou le bas jusqu'ici, parce qu'elles ont la valeur score.

Ma question est de savoir comment ce comportement peut être modifié afin que la commande soit correcte.

+0

Pouvez-vous indiquer plus clairement quel est le problème?Avez-vous une erreur du code ci-dessus s'il n'y a pas de votes? Quelle est l'erreur? –

Répondre

14

Avant l'introduction des annotations, vous pourriez avoir utilisé extra de faire quelque chose comme ça, que je pense devrait revenir 0 dans les cas où il n'y a pas de voix (si elle n'a pas pour toute mise en œuvre de base de données particulière, vous peut au moins insérer directement l'appel de fonction COALESCE nécessaire - COALESCE(SUM(value), 0) - en utilisant cette méthode):

pictures = gallery.picture_set.extra(
    select={ 
     'score': 'SELECT SUM(value) FROM yourapp_picturevote WHERE yourapp_picturevote.picture_id = yourapp_picture.id', 
    }, 
    order_by=['-score'] 
) 

je ne vois aucune façon intégrée pour ajouter votre propre SQL à la nouvelle substance d'annotation (que je n » t personnellement utilisé encore), mais il semble que vous devriez être en mesure de créer une nouvelle annotation comme ceci:

from django.db.models import aggregates 
from django.db.models.sql import aggregates as sql_aggregates 

class SumWithDefault(aggregates.Aggregate): 
    name = 'SumWithDefault' 

class SQLSumWithDefault(sql_aggregates.Sum): 
    sql_template = 'COALESCE(%(function)s(%(field)s), %(default)s)' 

setattr(sql_aggregates, 'SumWithDefault', SQLSumWithDefault) 

Cela ressemble plutôt laid que vous devez monkey-patch le nouvel agrégat en django.db.models.sql.aggregates en raison de la façon dont les classes d'agrégats SQL sont recherchés, mais tout ce que nous avons fait ici est ajouté un nouvel agrégat qui sous-classes Sum, hardcoding un appel à la fonction COALESCE et l'ajout d'un espace réservé pour la valeur par défaut, que doit fournir en tant qu'argument mot-clé (dans cet exemple d'implémentation très basique, au moins).

Cela devrait vous permettre de faire ce qui suit:

pictures = gallery.picture_set.annotate(score=SumWithDefault('picturevote__value', default=0).order_by('-score') 
+0

Votre deuxième approche fonctionne très bien! Merci :) – okoman

+0

Bonjour. C'est après un très long moment, cependant je pense qu'il y a une erreur dans votre sql_template. Vous devez l'écrire comme suit: sql_template = '% (fonction) s (COALESCE (% (champ) s,% (par défaut) s))'. S'il vous plaît pensez-y et dites-moi si vous êtes d'accord - cela me hante depuis des mois! – Serafeim

+0

django1.8 [supporte maintenant 'Coalesce'] (https://docs.djangoproject.com/fr/1.8/ref/models/database-functions/#coalesce) comme fonction de base de données – Matt

2

De Django 1.8, il y a une fonction de base de données Coalesce. Votre requête pourrait ressembler à ceci:

from django.db.models.functions import Coalesce  

score = self.picturevote_set.aggregate(Coalesce(models.Sum('value'), 0)) 
+0

Il y a aussi une demande de fonctionnalité ouverte, et commentaire du développeur core Django: https://code.djangoproject.com/ticket/10929#comment:16 –

Questions connexes