Tengo este código:

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)

Entonces, la salida será:

5 10 0.0
1 2 0.0

Pero yo necesito

5 10 0.5
1 2 0.5

Wy F () redondeó el resultado? ¿Cómo no hacer esto?

¡Gracias!

13
Lev 7 may. 2016 a las 20:48

4 respuestas

La mejor respuesta

El resultado que espera es realmente fácil de lograr con una consulta sin procesar y realmente, quiero decir, muy difícil de lograr con django puro.

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)

No acusaría esto de ser elegante, pero funciona tanto en Mysql como en Postgresql.

Para proporcionar algunos antecedentes. El redondeo lo realiza la base de datos haciendo división entera porque el campo que tiene son ints. Si desea la división decimal, debe convertirlos en decimales. Desafortunadamente, lanzar no es muy fácil con Django.

Postgresql tiene una forma realmente elegante de lanzar para flotar. value1 :: float pero no se puede usar desde dentro de django (al menos hasta donde yo sé)

7
e4c5 8 may. 2016 a las 15:43

En v1.10, Django introdujo un Cast función que hace que esto (casi) sea muy sencillo: la calificación es que necesita dos lanzamientos o debe envolver todo en un ExpressionWrapper para evitar un FieldError: Expression contains mixed types. You must set output_field.

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

Esto es funcionalmente equivalente a multiplicar el enumerador por 1.0, pero un reparto explícito aclara su intención más que una multiplicación por un número.

0
Endre Both 14 feb. 2019 a las 14:17

Desafortunadamente, la operación ORM F('value1') / F('value2') se ejecuta en el lado de la base de datos, por lo tanto, si ambos campos declarados como integer definitivamente obtendrá el resultado integer. En Django 1.11.7 puedes simplemente lanzar una de las expresiones F() a decimal así:

 qs = (
     MyModel.objects
     .annotate(
         res=ExpressionWrapper(
             F('value1') * 1.0 / F('value2'), 
             output_field=FloatField(),
         ),
     )
 )
2
Alex-Bogdanov 30 may. 2018 a las 12:01

Simplemente use el soporte de F() para la multiplicación para convertir un factor a un número decimal.

La expresión combinada se vería así:

from decimal import Decimal

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

Encuentro esta forma más elegante, luego escribo SQL CAST sin procesar en el campo value1 y luego hago la división.

14
joanbm 8 may. 2016 a las 13:46