Django F () division - как избежать округления

у меня есть этот код:

q = MyModel.objects.order_by('-value1').annotate(
            res=ExpressionWrapper(
                (F('value1') / F('value2')), 
                output_field=FloatField()),
            )

for i in q:                                          
    print(i.value1, i.value2, i.res)

Итак, выход будет:

5 10 0.0
1 2 0.0

но мне нужно

5 10 0.5
1 2 0.5

Wy F () округленный результат? Как не сделать этого?

спасибо!

3 ответов


результат, который вы ожидаете, очень легко достичь с помощью raw-запроса и действительно, я имею в виду очень трудно достичь с чистым django.

from django.db.models import FloatField, ExpressionWrapper, F

template = '%(function)s(%(expressions)s AS FLOAT)'
fv1 =  Func(F('value1'), function='CAST', template=template)
fv2 =  Func(F('value2'), function='CAST', template=template)
ew = ExpressionWrapper(fv1/fv2, output_field = FloatField())


q = MyModel.objects.order_by('-value1').annotate(res = ew)

вы бы не обвинили это в элегантности, но он работает как на Mysql, так и на Postgresql.

чтобы обеспечить некоторый фон. Округление выполняется базой данных doing целочисленное деление потому что поле у вас есть ints. Если вы хотите десятичное деление, вам нужно привести их к десятичным дробям. К сожалению, литье не очень легко с Django.

Postgresql имеет действительно элегантный способ бросить плавать. value1:: float но это нельзя использовать изнутри django (по крайней мере, насколько я знаю)


просто использовать F() поддержка умножения для преобразования одного фактора в десятичное число.

комбинированное выражение тогда будет выглядеть так:

from decimal import Decimal

q = MyModel.objects.order_by('-value1').annotate(
            res=ExpressionWrapper(
                (F('value1') * Decimal('1.0') / F('value2')), 
                output_field=FloatField()),
            )

Я нахожу этот более элегантный способ, затем пишу raw SQL CAST на поле value1, а затем делаю разделение.


к сожалению, ORM F('value1') / F('value2') операция выполняется на стороне базы данных, поэтому если оба поля объявлены как integer вы обязательно получите integer результат. В Django 1.11.7 вы можете просто бросить один из F() выражение decimal такой:

 qs = (
     MyModel.objects
     .annotate(
         res=ExpressionWrapper(
             F('value1') * 1.0 / F('value2'), 
             output_field=FloatField(),
         ),
     )
 )