Django query - "case when" с функцией агрегации
у меня есть следующая модель django (сопоставленная с таблицей 'A'):
class A(models.Model):
name = models.CharField(max_length=64, null=False)
value = models.IntegerField()
...
Я хочу выполнить следующий простой запрос сверху:
select avg(case
when (value > 0 and value <= 50) then 0
when (value > 50 and value < 70) then 50
else 100 end)
from A
where ...
Я пытаюсь избежать raw SQL-как это можно реализовать с django (в приведенном выше примере я использую avg, но тот же вопрос также актуален для max, min, sum и т. д.)?
Я попытался использовать extra и aggregate:
extra(select={'avg_field': case_when_query})
и
aggregate(Avg('avg_field')),
но агрегатная функция работает только с полями модели, поэтому дополнительное поле не может использоваться здесь. Как это можно сделать с Django?
Спасибо за помощь
3 ответов
насколько я знаю, (к сожалению) нет способа сделать то, что вы описали, не прибегая к raw SQL.
сказал, что там и способ вычисления среднего так, как вы описываете, если вы готовы денормализовать свои данные немного. Например, вы можете добавить новый столбец с именем average_field
это автоматически устанавливается в соответствующее значение на save()
. Вы можете либо переопределить save()
или нажмите на сигнал, чтобы сделать это автоматически. Для например,
class A(models.Model):
name = models.CharField(max_length=64, null=False)
value = models.IntegerField()
average_field = models.IntegerField(default = 0)
def _get_average_field(self):
# Trying to match the case statement's syntax.
# You can also do 0 < self.value <= 50
if self.value > 0 and self.value <= 50:
return 0
elif self.value > 50 and self.value < 70:
return 50
else:
return 100
def save(self, *args, **kwargs):
if self.value:
self.average_field = self._get_average_field()
super(A, self).save(*args, **kwargs)
как только вы это сделаете, ваш запрос станет очень легким.
A.objects.filter(...).aggregate(avg = Avg('average_field'))
что можно сделать, что все равно позволит нам использовать Django queryset что-то вроде этого:
qs = A.objects.extra(select={"avg_field":
"avg(case when...)"}).filter(...).values("avg_field")
использовать результат:
qs[0]["avg_field"]
и это позволит необходимую функциональность.
Django 1.8 будет поддерживать случай, когда выражения из коробки, см. https://docs.djangoproject.com/en/dev/ref/models/conditional-expressions/