2009-02-16 5 views
5

Я использую следующий фрагмент кода в моем приложении Django:Treat NULL как «0» в Django модели

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

Существует таблица галерей. В каждой из них есть несколько фотографий. Когда пользователь голосует вверх или вниз по картинке, новая строка в «picturevote» вставлена ​​и подключена к изображению. Затем я могу получить общий балл для фотографий. Теперь я хочу заказать фотографии одной галереи по их значениям. Но из-за объединения таблиц может быть значение NULL для оценки, когда не было голосов вообще. Тем не менее оценка «NULL» будет считаться «0».

Любые идеи?

редактировать: Хорошо, вот некоторые дополнительные объяснения: Проблема заключается в том, что агрегация в приведенном выше примере устанавливает score в NULL. Когда я хочу, чтобы отобразить счет я использую что-то вроде этого:

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

Затем агрегирование приводит либо к NULL (если нет picturevote строк) или определенное значение. Если он равен NULL, выражение or преобразует его в отображаемое целочисленное значение. Но это решает только проблемы с отображением, вызванные значением NULL. Когда я хочу сортировать фотографии по этому значению score, как в первом примере кода все записи с NULL помещаются в конец упорядоченного набора результатов. Сначала есть фотографии с положительными оценками, тогда есть изображения с отрицательными значениями, и ТОГДА есть фотографии, которые не были проголосованы вверх или вниз, потому что у них есть NULL как score.

Мой вопрос заключается в том, как это поведение можно изменить, чтобы порядок был правильным.

+0

Можете уточнить, в чем проблема?Получаете ли вы ошибку из приведенного выше кода, если нет голосов? Какая ошибка? –

ответ

14

До введения аннотации, вы могли бы использовать extra, чтобы сделать что-то вроде этого, что я думать должен вернуться 0 в тех случаях, когда нет голосов (если это не для какой-либо конкретной реализации базы данных, может по крайней мере, непосредственно вставить необходимую COALESCE вызова функции - COALESCE(SUM(value), 0) - с помощью этого метода):

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

Я не могу увидеть любой встроенный способ, чтобы добавить свой собственный SQL к новым аннотациям вещи (которые я гавань» t лично используется еще), но похоже, что вы должны иметь возможность создать новую аннотацию, например:

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) 

Это выглядит довольно уродливым, как вам нужно monkeypatch нового агрегата в django.db.models.sql.aggregates благодаря тому, как классы агрегатного SQL ищутся, но все, что мы сделали здесь добавляется новый агрегат, который подкласс Sum, жестко прописывать вызов функции COALESCE и добавление заполнителя для значения по умолчанию, которое вы должно поставлять в качестве аргумента ключевого слова (по крайней мере, в этом очень основном примере реализации).

Это должно позволить вам сделать следующее:

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

Ваш второй подход отлично работает! Спасибо :) – okoman

+0

Здравствуйте. Это происходит очень долго, однако я думаю, что в вашем sql_template есть ошибка. Вы должны написать это следующим образом: sql_template = '% (function) s (COALESCE (% (поле) s,% (по умолчанию) s))'. Пожалуйста, подумайте об этом и скажите мне, согласны ли вы - это преследовало меня в течение нескольких месяцев! – Serafeim

+0

django1.8 [теперь поддерживает 'Coalesce'] (https://docs.djangoproject.com/en/1.8/ref/models/database-functions/#coalesce) в качестве функции базы данных – Matt

2

С Django 1.8, есть функция Coalesce базы данных. Ваш запрос может выглядеть так:

from django.db.models.functions import Coalesce  

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

Существует также открытый запрос функции и комментарий разработчика ядра Django: https://code.djangoproject.com/ticket/10929#comment:16 –