How do I have actions occur when a field gets changed in one of my models? In this particular case, I have this model:
class Game(models.Model): STATE_CHOICES = ( ('S', 'Setup'), ('A', 'Active'), ('P', 'Paused'), ('F', 'Finished') ) name = models.CharField(max_length=100) owner = models.ForeignKey(User) created = models.DateTimeField(auto_now_add=True) started = models.DateTimeField(null=True) state = models.CharField(max_length=1, choices=STATE_CHOICES, default='S')
and I would like to have Units created, and the 'started' field populated with the current datetime (among other things), when the state goes from Setup to Active.
I suspect that a model instance method is needed, but the docs don't seem to have much to say about using them in this manner.
Update: I've added the following to my Game class:
def __init__(self, *args, **kwargs): super(Game, self).__init__(*args, **kwargs) self.old_state = self.state def save(self, force_insert=False, force_update=False): if self.old_state == 'S' and self.state == 'A': self.started = datetime.datetime.now() super(Game, self).save(force_insert, force_update) self.old_state = self.state
Basically, you need to override the
save method, check if the
state field was changed, set
started if needed and then let the model base class finish persisting to the database.
The tricky part is figuring out if the field was changed. Check out the mixins and other solutions in this question to help you out with this:
It has been answered, but here's an example of using signals, post_init and post_save.
from django.db.models.signals import post_save, post_init class MyModel(models.Model): state = models.IntegerField() previous_state = None @staticmethod def post_save(sender, **kwargs): instance = kwargs.get('instance') created = kwargs.get('created') if instance.previous_state != instance.state or created: do_something_with_state_change() @staticmethod def remember_state(sender, **kwargs): instance = kwargs.get('instance') instance.previous_state = instance.state post_save.connect(MyModel.post_save, sender=MyModel) post_init.connect(MyModel.remember_state, sender=MyModel)