Search code examples
djangodjango-signals

Django: passing variables from pre_save to post_save signals


I use the pre_save and post_save signals to send analytics to Mixpanel. I prefer to keep this separated from my model's save method.

Is there a way to save the old values of an instance when the pre_save signal occurs, and then check the new values against them on post_save?

My code looks like this:

@receiver(pre_save, sender=Activity)
def send_user_profile_analytics(sender, **kwargs):
    activity_completed_old_value = kwargs['instance'].is_completed
    # store this value somewhere?

@receiver(post_save, sender=Activity)
def send_user_profile_analytics(sender, **kwargs):
    if kwargs['instance'].is_completed != activity_completed_old_value:
        # send analytics

For me it seems more robust to use post_save to send the analytics than pre_save, but at that point I can't see what has changed in the model instance. I would like to prevent using globals or implementing something in my model's save function.


Solution

  • You can store them as instance attributes.

    @receiver(pre_save, sender=Activity)
    def send_user_profile_analytics(sender, **kwargs):
        instance = kwargs['instance']
        instance._activity_completed_old_value = instance.is_completed
    
    @receiver(post_save, sender=Activity)
    def send_user_profile_analytics(sender, **kwargs):
        instance = kwargs['instance']     
        if instance.is_completed != instance._activity_completed_old_value:
            # send analytics
    

    In this way you "send analytics" only if is_completed changes during save (that means that save doesn't just store the value but makes some further elaboration).

    If you want to perform an action when a field is changed during instance life-time (that is from its creation till the save) you should store the initial value during post_init (and not pre_save).