在 Django rest 中将整数格式化为注释内的百分比

format count integer into percentage inside annotate in Djnago rest

我必须在 DRF 的 API 调用中发送计数百分比。我已经使用注释计算了计数并附加到查询集。但实际上我需要的是百分比而不是计数。

TYPES = ((1,'cold'),
         (2,'humid'),
         (3,'hot'))

from django.db.models import Q,Count

class Destinations(models.Model):

   continent = models.CharField()
   /............/


class Packages(models.Model):

    location = models.Foreignkey(Destination,on_delete=models.CASCADE,related_name='packages')
    place_type = models.CharField(
                            max_length=1,
                            choices=TYPES,
                            default=""

我的看法:

我在 modelviewset 中使用了列表函数,像这样:

    Destiantion = Destination.objects.all().annotate(total_packages=Count('packages'),
                                                               
 cold_count=Count('packages',filter=Q(packages__place_type=1)),                                                                   
 humid_count=Count('packages',filter=Q(packages__place_type=2)),
 hot_count =Count('packages',filter=Q(packages__place_type=3)))

在这里,我得到的响应是属性中包类型的计数,但我想要的是包类型的百分比,如 25%,方法是 cold_count*100/total_packages,但不能在注释中使用它。

有一个 count() 方法可能有用,但如果我使用它,我必须进行 4 个单独的查询,并且可能为此编写另一个 API。所以我必须使用注释。但是怎么办呢??

你应该试试

from django.db.models.functions import Coalesce

Destination.objects.all().annotate(
    total_packages=Count('packages'),                                                          
    cold_count_percent=Coalesce(100.0 * Count('packages',filter=Q(packages__place_type=1)) / Count('packages'), 0),                                                                   
    humid_count_percent=Coalesce(100.0 * Count('packages',filter=Q(packages__place_type=2)) / Count('packages'), 0),                                    
    hot_count_percent=Coalesce(100.0 * Count('packages',filter=Q(packages__place_type=3)) / Count('packages'), 0),                                    
)

此外,如果你需要处理 Count('packages')=0 时的情况,你需要这样的东西:

from django.db.models.functions import Coalesce

Destination.objects.all().annotate(
    total_packages=Count('packages'),                                                          
    cold_count_percent=Coalesce(div_zero(100.0 * Count('packages',filter=Q(packages__place_type=1)) / Count('packages'), 'total_packages'), 0),                                                                       
    humid_count_percent=Coalesce(div_zero(100.0 * Count('packages',filter=Q(packages__place_type=2)) / Count('packages'), 'total_packages'), 0),                                    
    hot_count_percent=Coalesce(div_zero(100.0 * Count('packages',filter=Q(packages__place_type=3)) / Count('packages'), 'total_packages'), 0),                                   
)

其中 div_zero 如下:

from django.db.models import Case, Value, FloatField
from django.db.models.expressions import When

INFINITY = 1000000000000 
def div_zero(result, divisor):
    return Case(
        When(**{
            divisor: 0,
            'then': Value(INFINITY, FloatField()),
        }),
        default=result,
        output_field=FloatField(),
    )