Django count and store values in models

Question:

I have multiple Models which look like this:

class Classes(models.Model):
User = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
A1 = models.IntegerField(default=0)
B1 = models.IntegerField(default=0)
etc
A2 = models.IntegerField(default=0)
B2 = models.IntegerField(default=0)
etc
A3 = models.IntegerField(default=0)
B3 = models.IntegerField(default=0)
etc
A4 = models.IntegerField(default=0)
B4 = models.IntegerField(default=0)
etc
Sum_of_1 = models.IntegerField( blank=True, null=True)
Sum_of__2 = models.IntegerField( blank=True, null=True)
Sum_of__3 = models.IntegerField( blank=True, null=True)
Sum_of__4 = models.IntegerField( blank=True, null=True)

its a bit bigger, anyways, I want A and B etc sum together and save the result in sum of 1 etc. First I thought that a form would be good for that but the sum fields should always have the sum they never should be updated so then I thought rewriting the save method in models, I got this but it just gives me an recursion error I know why but have no solution for that

super().save(*args, **kwargs)
if self.Sum_of__1 or self.Sum_of__2 or self.Sum_of__3 or self.Sum_of__4 is None:
self.Sum_of__1 = self.A1 + self.B1 + self.C1 + self.D1 + self.E1
self.Sum_of__2 = self.A2 + self.B2 + self.C2 + self.D2 + self.E2
self.Sum_of__3 = self.A3 + self.B3 + self.C3 + self.D3 + self.E3
self.Sum_of__4 = self.A4 + self.B4 + self.C4 + self.D4 + self.E4
self.save()

Any ideas?

Asked By: random1312

||

Answers:

You can make a call to the super().save(*args, **kwargs) after updating the sums, so:

def save(self, *args, **kwargs):
    if self.Sum_of_1 is None or self.Sum_of_2 is None or self.Sum_of_3 is None or self.Sum_of_4 is None:
        self.Sum_of_1 = self.A1 + self.B1 + self.C1 + self.D1 + self.E1
        self.Sum_of_2 = self.A2 + self.B2 + self.C2 + self.D2 + self.E2
        self.Sum_of_3 = self.A3 + self.B3 + self.C3 + self.D3 + self.E3
        self.Sum_of_4 = self.A4 + self.B4 + self.C4 + self.D4 + self.E4
    super().save(*args, **kwargs)

But it is a really bad idea to store aggregates: it introduces a form of data duplication, and will result in a lot of trouble to keep the values in sync with each other: if you later change for example B1, you will need to updater Sum_of_1 as well. While that may look easy, there are several ways through the ORM, or through raw database queries, or other applications, that will circumvent the .save() call, and thus eventually the data will no longer be synced correctly. It might be better to just define a property, like:

from django.conf import settings

class MyModel(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MDOEL,
        on_delete=models.SET_NULL,
        null=True
    )
    A1 = models.IntegerField(default=0)
    B1 = models.IntegerField(default=0)
    # etc.
    A2 = models.IntegerField(default=0)
    B2 = models.IntegerField(default=0)
    # etc.
    A3 = models.IntegerField(default=0)
    B3 = models.IntegerField(default=0)
    # etc.
    A4 = models.IntegerField(default=0)
    B4 = models.IntegerField(default=0)
    # etc.
    
    @property
    def sum_of_1(self):
        return self.A1 + self.B1 + self.C1 + self.D1 + self.E1
    
    @property
    def sum_of_1(self):
        return self.A1 + self.B1 + self.C1 + self.D1 + self.E1
    
    @property
    def sum_of_2(self):
        return self.A2 + self.B2 + self.C2 + self.D2 + self.E2
    
    @property
    def sum_of_3(self):
        return self.A3 + self.B3 + self.C3 + self.D3 + self.E3
    
    @property
    def sum_of_4(self):
        return self.A4 + self.B4 + self.C4 + self.D4 + self.E4
Answered By: Willem Van Onsem