Django filter tasks done in project ListView

I have ListView of projects that has tasks i want to calculate progress % of each project i have made it like this code bellow but the progress calculate all tasks even that is not related to project haw can i do it for the tasks that is related to the project
Models:

class Project(models.Model):
    name = models.CharField(_("Product name"), max_length=255)
    selcted_user = models.ForeignKey(
        User, on_delete=models.CASCADE, limit_choices_to={'is_staff': False})

    product_content = models.CharField(
        _("Product content"), max_length=255)
    order_number = models.CharField(
        _("Order number"), max_length=255)
    amount = models.FloatField(_("Amount $"))
    product_image = models.ImageField(
        _("Product image"), upload_to="projects/%Y/%m/%d/", default='default-thumb.png')
    date = models.DateTimeField(auto_now_add=True)

    class Status(models.TextChoices):

        WORKING = '1', 'Working'
        DONE = '2', 'Done'
        PENDING = '3', 'Pending'
    status = models.CharField(
        max_length=7, choices=Status.choices, default=Status.WORKING)

    class Meta:
        ordering = ['name']

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('project-detail', kwargs={'pk': self.pk})


class Task(models.Model):
    name = models.CharField(max_length=255)
    project = models.ForeignKey(
        Project,  on_delete=models.CASCADE, related_name='task_project')

    class Done(models.TextChoices):
        NO = '1', 'No'
        WORKING = '2', 'Working'
        YES = '3', 'Yes'

    is_done = models.CharField(
        max_length=3, choices=Done.choices, default=Done.WORKING)

    class Meta:
        ordering = ['project', 'name']

    def __str__(self):
        return self.name

Views:

class ClientProjectView(LoginRequiredMixin, SuccessMessageMixin, ListView):
model = Project
template_name = 'project/list.html'

def get_context_data(self, *args, **kwargs):
    context = super().get_context_data(*args, **kwargs)
    context['tasks'] = Task.objects.all()
    if Task.objects.exists():
        task_done = Task.objects.filter(is_done='3', project_id__in=self.object_list).count()* 100 / Task.objects.filter(project_id__in=self.object_list).count()
        context['percentage_done'] = task_done
    else:
        context['percentage_done'] = '0'
    return context

templates:

<div class="progress" style="height: 25px; border-radius: 20px">
     <div class="progress-bar bg-success" role="progressbar" style="width: {{percentage_done}}%;"
                                                aria-valuenow="{{percentage_done}}" aria-valuemin="0" aria-valuemax="100">{{percentage_done|floatformat:1}}%</div>
</div>

It looks to me like what you’re trying to do is calculate a separate percentage for each of the projects in the list, is that correct?

If so, then what you probably want to do is annotate that percentage complete value in the queryset for Project.

See Aggregation | Django documentation | Django to get started. Also see QuerySet API reference | Django documentation | Django and Query Expressions | Django documentation | Django

1 Like

Thank you for your answer, but can you give me an example? I don’t really know how to do it in my case.

Review the examples in the docs. Try using the shell to experiment with some queries. If you get stuck with a particular element, post here what you’ve tried and we’ll see if we can help.

Since you haven’t posted your models, it’s tough to provide an accurate example, but as a starting point you should be able to play with:
project_queryset = Project.objects.annotate(task_count=Count('task'))

This should create an attribute in each element of project_queryset called task_count which contains the number of tasks associated with that project.

1 Like

Thanks again. I will try to do what you suggested. I edited the post and added the templates with the views and the models too. hope it will work

hello again in detail view i have made success to calculate the percentage of it with this code

progress = Task.objects.filter(is_done='3', project_id=self.object.pk).count(
            ) * 100 / Task.objects.filter(project_id=self.object.pk).count()

but the list view still has the same problems

So what have you tried in the list view to annotate a progress calculation in the query?

this haw i did it

Project.objects.annotate(task_count=Count('task_project', filter=Q(task_project__is_done='3')) / Count('task_project') * 100)

And are you saying that this is, or isn’t working for you? (If it’s not, what’s not working?)

It still calculates all the tasks even if they are unrelated to the project.
And what I need is to calculate the percentage of tasks that are done for each project alone in the list view of the projects.

You’re saying that each of the task_count field within each of the individual Project objects are still showing totals for all tasks?

hi again, will I did this code and it works fin in terminal it give me the result that i want

project = Project.objects.all()
        task = Task.objects.all()
        for t in task:
            for p in project:
                if t.project.id == p.id:
                    if t.is_done == '3':
                        all_project = p.id
                        total_progress = task.filter(project_id=all_project, is_done='3').count() * 100 /  task.filter(project_id=all_project).count()
                        
                        print(t.name, p.id, total_progress)
                    else:
                       context['progress_done'] = 0 

now I want to render this in the template like the code above haw can I do it

<table class="table text-nowrap" id="tableX" >
                            <thead>
                                <tr>
                                  
                                    <th scope="col" class="text-muted" style="font-weight: 300">{% translate "Order №" %}</th>
                                    <th scope="col" class="text-muted" style="font-weight: 300">{% translate "Order date" %}</th>
                                    <th scope="col" class="text-muted" style="font-weight: 300">{% translate "Product" %}</th>
                                    <th scope="col" class="text-muted" style="font-weight: 300">{% translate "Amount" %}</th>
                                    <th scope="col" class="text-muted" style="font-weight: 300">{% translate "Status" %}</th>
                                    <th class="text-muted" style="font-weight: 300">{% translate "Tasks progress" %}</th>
                                    <th class="text-muted" style="font-weight: 300">{% translate "% Progress" %}</th>
                                </tr>
                            </thead>
                            <tbody>
                              
                                    <tr>
                                        <td style="width:100px">{{p.order_number}}</td>
                                        <td style="width:100px">{{p.date|date:"d/m/Y" }}</td>
                                        
                                        <td style="width:150px;">
                                          
                                            <strong>{{p.name|truncatechars:15}}</strong>
                                          
                                        </td>
                                        
                                        <td style="width:100px">${{p.amount}}</td>
                                        <td style="width:100px">
                                          {% if p.status == '1'  %}
                                          <span class="bg-primary rounded-pill text-white py-1 px-3 ">In Production</span>
                                          {% elif p.status == '2'  %}
                                          <span class="bg-success rounded-pill text-white py-1 px-4 ">&nbsp;Complete&nbsp;</span>
                                          {% else %}
                                          <span class="bg-warning rounded-pill text-white py-1 px-4 ">&nbsp;&nbsp;Pending&nbsp;&nbsp;</span>
                                          {% endif %}
                                          
                                          
                                        </td>
                                        <td style="width:150px">
                                          {% for task in tasks %}
                                          {% if task.project.id == p.id%}
                                          <div class="container">
                                            <div class="row mb-2">
                                                <div class="col-md-1">
                                                    {% if task.is_done == '1' %}
                                                    <i class="fa-solid fa-clock  fa-lg text-warning"></i>
                                                    {% elif task.is_done == '2' %}
                                                    <i class="fa-solid fa-gears  fa-lg text-info"></i>
                                                    {% else %}
                                                    <i class="fa-solid fa-circle-check fa-lg  text-success"></i>
                                                    {% endif %}
                                                </div>
                                                <div class="col-md-6">
                                                    <strong>{{task.name}}</strong> 
                                                </div>
                                            </div>
                                          </div>
                                          
                                          {% endif %}
                                          {% endfor %}
                                        </td>
                                        <td>
                                      {{progress_done}}
                                          <div class="progress" style="height: 25px; border-radius: 20px">
                                            <div class="progress-bar bg-success" role="progressbar" style="width: {{progress_done}}%;"
                                                aria-valuenow="{{progress_done}}" aria-valuemin="0" aria-valuemax="100">{{progress_done|floatformat:1}}%</div>
                                        </div>
                                        
                                        </td>
                                        
                                    </tr>
                                
                            </tbody>
                        </table>