Django, разделение между двумя комментариями не будет правильно вычисляться

4 viviwill [2015-08-28 06:25:00]

Я пытаюсь получить разделение между двумя аннотированными результатами в запросе. Впечатление намного больше, чем щелчок, поэтому я должен получить десятое десятичное число.

def get_queryset(self):
        return googleData.objects.filter(account=self.account_name).\
            values('date').\
            annotate(Sum('click'), Sum('impression'), Sum('converted_click'),
                     Sum('conversion_value'), Sum('cost'), Sum('conversion_value'), ctr_monthly= Sum('click')/Sum('impression')).\
            order_by('-date')

Проблема здесь:

ctr_monthly= Sum('click')/Sum('impression'))

В шаблоне у меня есть:

<td>{{ googleData.ctr_monthly | floatformat:2}} </td>

И результат 0.00. Если я делаю ctr_final = click * impression, он генерируется правильно. Click и Impression являются целыми полями.

Я попытался использовать float(), дал мне синтаксическую ошибку.

Другой вопрос:, какая лучшая оценка для создания такого набора запросов? Есть ли способ, которым я могу разбить его на несколько коротких фрагментов кода и сделать его более аккуратным и удобочитаемым?

Спасибо ~

python floating-point django


2 ответа


10 Решение Bobort [2016-11-11 18:01:00]

С более новыми версиями Django вы можете использовать новый объект Func для передачи значений FloatFields или DecimalFields до Sum.

from django.db.models.functions import Cast
from django.db.models import FloatField
ctr_monthly= Cast(Sum('click'), FloatField())/Cast(Sum('impression')), FloatField())

Даже с более старой версией Django вы можете просто указать output_field на Sum перед тем, как аннотировать ctr_monthly так:

from django.db.models import F
def get_queryset(self):
    return googleData.objects.filter(
        account=self.account_name
    ).values('date').annotate(
        click_sum=Sum(
            'click',
            output_field=FloatField()
        ),
        impression_sum=Sum(
            'impression',
            output_field=FloatField()
        ),
        converted_click_sum=Sum('converted_click'),
        conversion_value_sum=Sum('conversion_value'),
        cost_sum=Sum('cost')
    ).annotate(
        ctr_monthly=F('click_sum') / F('impression_sum')
    ).order_by('-date')

1 Jon Combe [2015-08-28 07:12:00]

Насколько мне известно, нет способа сделать это с помощью ORM.

Функция Sum() возвращает тот же тип поля, который помещается в него (т.е. IntegerField() всегда возвращает Integer). Вы можете использовать функцию типа ExpressionWrapper, чтобы заставить выход быть float, но это не поможет в этом случае, поскольку будет слишком поздно: деление двух целых чисел уже будет возвращать другое целое число.

Чтобы решить вашу проблему, удалите раздел ctr_monthly, который формирует ваш запрос, и создайте простой тег шаблона, который преобразует два числа в плавающие и разложит их.

Затем ваш шаблон будет выглядеть так: <td>{{ monthly_ctr(googleData.click, googleData.impression) | floatformat:2}} </td>