John R Perry
John R Perry

Reputation: 4192

When does the save method in a Django model not run?

I have a Django model that overrides the save method and looks like this:

class Log(models.Model):
    agent = models.ForeignKey(Agent, on_delete=models.CASCADE)
    calls_logged = models.IntegerField(default=0)
    texts_logged = models.IntegerField(default=0)

    completed_logs = models.BooleanField(default=False, db_index=True)

    def save(self, *args, **kwargs):
        if self.calls_logged >= 30 and self.texts_logged >= 50:
            self.completed_logs = True
        super().save(*args, **kwargs)

What's supposed to happen is when the Agent reaches 30 calls and 50 texts, the save() method is supposed to change the completed_logs field to True. But when I recently checked my DB, an agent had reached over 30 calls and over 50 texts but completed_logs wasn't marked as completed. However, when I clicked 'Save' from within the admin, it updated the completed_logs field as expected.

So, my question is: is there ever a time that Django will not call the save() method on a model? Is there a better way to do this?

Upvotes: 1

Views: 1225

Answers (1)

Abdul Aziz Barkat
Abdul Aziz Barkat

Reputation: 21817

There are some methods that can update entries in the database without calling the save method. Some of which I can think of are:

  • The update method of a queryset
  • The bulk_create method of a queryset
  • The bulk_update method of a queryset

Most likely the situation is that you call one of these methods, causing this situation to occur.

The best thing to do here is to not have completed_logs as a field but rather as a property:

class Log(models.Model):
    agent = models.ForeignKey(Agent, on_delete=models.CASCADE)
    calls_logged = models.IntegerField(default=0)
    texts_logged = models.IntegerField(default=0)
    
    @property
    def completed_logs(self):
        return self.calls_logged >= 30 and self.texts_logged >= 50

Upvotes: 4

Related Questions