'Django aggregate sum of manytomany is adding up everything in its field instead of the ones selected

2 Classes involved in question class Appointment and class Service

appointmentApp.models class Service

class Service(models.Model):
    service_name = models.CharField(max_length=15, blank=False)
    service_time = models.IntegerField(blank=False)
    
    def __str__(self):
        return self.service_name

    class Meta:
        verbose_name_plural = "Services"

appointmentApp/models.py class Appointment

class Appointment(models.Model):
    service_chosen = models.ManyToManyField(Service, blank=False)
    total_time = models.IntegerField(blank=False, null=False, default=0)

    #will add up the amount of time needed for each service
    def save(self, *args, **kwargs):
        self.total_time += Service.objects.all().aggregate(total_time=Sum('service_time'))['total_time']
        super(Appointment, self).save(*args, **kwargs)

    def __str__(self):
        return self.client_dog_name

Services are chosen through a multiplechoice field and on save the service_chosen's service_time are added up

but what my save function is doing instead is adding up all the existing service.service_time instead of the ones selected, why is this happening?



Solution 1:[1]

ManyToManyFields are saved after the containing instance is saved, you need to create a signal handler to perform this update on m2m_changed

from django.db.models.signals import m2m_changed


class Appointment(models.Model):
    ...


def service_chosen_changed(sender, instance=None, action=None, **kwargs):
    if action == 'post_add':
        instance.total_time = instance.service_chosen.aggregate(total_time=Sum('service_time'))['total_time']
        instance.save()

m2m_changed.connect(service_chosen_changed, sender=Appointment.service_chosen.through)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Iain Shelvington