Формат целого числа count в проценты внутри аннотации в Djnago rest

Мне нужно отправить процент подсчетов в вызове API в DRF. Я подсчитал количество и прикрепил к набору запросов с помощью annotate. Но на самом деле мне нужен процент, а не количество.

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=""

Мое мнение:

Я использую функцию list внутри 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, но не могу использовать это внутри annotate.

Есть метод count(), который может быть полезен, но если я использую его, мне придется сделать 4 отдельных запроса и, возможно, написать еще один API только для этого. Поэтому я должен использовать annotate. Но как?

Вам стоит попробовать

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(),
    )
Вернуться на верх