hi i am getting the progress percentage of each project in a ListView with subquery with the following code
class projects(LoginRequiredMixin, ListView):
model = Project
template_name = 'project_list.html'
ordering = ['project_title']
paginate_by = 10
queryset = Project.objects.annotate(
todo_done=Count('todo', filter=Q(todo__state=True)) * 100 / Count('todo'),
todo_left=Count('todo', filter=Q(todo__state=False)) * 100 / Count('todo'),
)
in a project I have 12 tasks, 8 finished and 4 in progress. The system returns 66% completed and 33% in progress, whose sum is 99% and not 100%
{{ project.todo_done }}
{{ project.todo_left }}
part of the bar is blank because the 1% is missing. I try to use round as follows but it is not possible
todo_done=round(Count('todo', filter=Q(todo__state=True)) * 100 / Count('todo')),
TypeError: type CombinedExpression doesn't define __round__ method
You can not use Python's round
, since that does not understand anything about database expressions. However you can make use of a Round
expression [Django-doc]:
from django.db.models import F
from django.db.models.functions import Round
queryset = Project.objects.annotate(
todo_done=Round(Count('todo', filter=Q(todo__state=True)) * 100 / Count('todo')),
todo_left=100*Count('todo') - F('todo_done'),
)
If the state
of the Todo
s can only be True
or False
, it is probably better to subtract todo_done
from 100 times Count('todo')
, since then it is normally guaranteed that the two will sum up to 100, unless of course there are no related Todo
s.
Some databases will use integer division if the two operands are integers, you can prevent this by casting it to a FloatField
:
from django.db.models import F, FloatField
from django.db.models.functions import Cast, Round
queryset = Project.objects.annotate(
todo_done=Round(
Cast(Count('todo', filter=Q(todo__state=True)), output_field=FloatField())
* 100 / Count('todo')
),
todo_left=100*Count('todo') - F('todo_done'),
)