I use the following bit of code in my Django app:
pictures = gallery.picture_set.annotate( score=models.Sum( 'picturevote__value' ) ).order_by( '-score' )
There is a table of galleries. In each of them are some pictures. When a user votes up or down a picture, a new row in 'picturevote' is inserted and connected to the picture. Then I can get the total score for the pictures. Now I want to order the pictures of one gallery by their score values. But due to the table joins there can be the value NULL for score when there were no votes at all. Nevertheless a score of 'NULL' shall be treated as '0'.
Any ideas?
edit:
Okay, here some additional explanation:
The problem is that the aggregation in the above example sets score
to NULL. When I want to display the score I use something like this:
score = self.picturevote_set.aggregate( models.Sum( 'value' ) )[ 'value__sum' ] or 0
Then the aggregation leads either to NULL (if there are no picturevote rows) or a certain value. If it is NULL the or-expression converts it to a displayable integer value.
But this solves just the display problems which are caused by the NULL value.
When I want to sort the pictures by this score
value as in the first code example all entries with NULL are put at the end of the ordered result set. First there are pictures with positive scores, then there are the pictures with negative values and THEN there are the pictures that were not voted up or down so far, because they have NULL as score
.
My question is how this behaviour can be changed so that the order is correct.
From Django 1.8, there is a
Coalesce
database function. Your query might look like this:Prior to the introduction of annotations, you might have used
extra
to do something like this, which I think should return0
in cases where there are no votes (if it doesn't for any particular database implementation, you can at least directly insert the necessaryCOALESCE
function call -COALESCE(SUM(value), 0)
- using this method):I can't see any built-in way to add your own SQL to the new annotation stuff (which I haven't personally used yet), but it looks like you should be able to create a new annotation like so:
This looks rather ugly as you need to monkeypatch the new aggregate into
django.db.models.sql.aggregates
due to the way the SQL aggregate classes are looked up, but all we've done here is added a new aggregate which subclassesSum
, hardcoding a call to theCOALESCE
function and adding a placeholder for the default value, which you must supply as a keyword argument (in this very basic example implementation, at least).This should let you do the following: