Identify the changed fields in django post_save signal

If you want to compare state before and after save action, you can use pre_save signal which provide you instance as it should become after database update and in pre_save you can read current state of instance in database and perform some actions based on difference.

from django.db.models.signals import pre_save
from django.dispatch import receiver


@receiver(pre_save, sender=MyModel)
def on_change(sender, instance: MyModel, **kwargs):
    if instance.id is None: # new object will be created
        pass # write your code here
    else:
        previous = MyModel.objects.get(id=instance.id)
        if previous.field_a != instance.field_a: # field will be updated
            pass  # write your code here

Ussually it's better to override the save method than using signals.

From Two scoops of django: "Use signals as a last resort."

I agree with @scoopseven answer about caching the original value on the init, but overriding the save method if it's possible.

class Mode(models.Model):
    name = models.CharField(max_length=5)
    mode = models.BooleanField()
    __original_mode = None

    def __init__(self, *args, **kwargs):
        super(Mode, self).__init__(*args, **kwargs)
        self.__original_mode = self.mode

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        if self.mode != self.__original_mode:
            #  then do this
        else:
            #  do that

        super(Mode, self).save(force_insert, force_update, *args, **kwargs)
        self.__original_mode = self.mode

UPDATE IF YOU NEED SIGNALS

Just in case you really need signals because you need a decoupled app or you can't simply override the save() method, you can use pre_save signal to 'watch' previous fields

@receiver(pre_save, sender=Mode)
def check_previous_mode(sender, instance, *args, **kwargs):
    original_mode = None
    if instance.id:
        original_mode = Mode.objects.get(pk=instance.id).mode
    
    if instance.mode != original_mode:
        #  then do this
    else:
        #  do that

The problem with this is that you make changes before, so if save() has a problem you could have some issues later. So to fix that issue, you can store the original value on the pre_save and use on post_save.

@receiver(pre_save, sender=Mode)
def cache_previous_mode(sender, instance, *args, **kwargs):
    original_mode = None
    if instance.id:
        original_mode = Mode.objects.get(pk=instance.id).mode
    
    instance.__original_mode = original_mode:

@receiver(post_save, sender=Mode)
def post_save_mode_handler(sender, instance, created, **kwargs):
    if instance.__original_mode != instance.original_mode:
        #  then do this
    else:
        #  do that

The problem with signals and this approach also is that you need one more query to check previous values.