Identify the changed fields in django post_save signal

前端 未结 7 1441
再見小時候
再見小時候 2020-12-02 15:28

I\'m using django\'s post_save signal to execute some statements after saving the model.

class Mode(models.Model):
    name = models.CharField(max_length=5)
         


        
相关标签:
7条回答
  • 2020-12-02 15:46

    Set it up on the __init__ of your model so you'll have access to it.

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

    Now you can perform something like:

    if instance.mode != instance.__original_mode:
        # do something useful
    
    0 讨论(0)
  • 2020-12-02 15:48

    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
    
    0 讨论(0)
  • 2020-12-02 15:51

    I'm late but it can be helpful for others.

    We can make custom signal for this.

    Using custom signal we can easily do these kind of things:

    1. Post is created or not
    2. Post is modified or not
    3. Post is saved but any field does not changed

       class Post(models.Model):
       # some fields 
    

    Custom signals

    **Make signal with arguments **

    from django.dispatch import Signal, receiver
    # provide arguments for your call back function
    post_signal = Signal(providing_args=['sender','instance','change','updatedfields'])
    

    Register signal with call back function

    # register your signal with receiver decorator 
    @receiver(post_signal)
    def post_signalReciever(sender,**kwargs):
        print(kwargs['updatedfields'])
        print(kwargs['change'])
    

    Sending the signal from post-admin

    We sending the signals from Post admin and also save object when it actually modified

    #sending the signals 
    class PostAdmin(admin.ModelAdmin):
       # filters or fields goes here 
    
       #save method 
       def save_model(self, request, obj, form, change):
    
    
        if not change and form.has_changed():  # new  post created
            super(PostAdmin, self).save_model(request, obj, form, change)
            post_signal.send(self.__class__,instance=obj,change=change,updatedfields=form.changed_data)
            print('Post created')
        elif change and form.has_changed(): # post is actually modified )
            super(PostAdmin, self).save_model(request, obj, form, change)
            post_signal.send(self.__class__,instance=obj,change=change,updatedfields=form.changed_data)
            print('Post modified')
        elif change and not form.has_changed() :
            print('Post not created or not updated only saved ')  
    

    See also:

    Django Signals official doc

    0 讨论(0)
  • 2020-12-02 15:58

    You can use update_fields in django signals.

    @receiver(post_save, sender=Mode)
    def post_save(sender, instance, created, **kwargs):
    
        # only update instance
        if not created:
    
            update_fields = kwargs.get('update_fields') or set()
    
            # value of `mode` has changed:
            if 'mode' in update_fields:
                # then do this
                pass
            else:
                # do that
                pass
    
    0 讨论(0)
  • 2020-12-02 15:59

    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
    
    0 讨论(0)
  • 2020-12-02 16:02

    in post_save method you have kwargs argument that is a dictionary and hold some information. You have update_fields in kwargs that tell you what fields changed. This fields stored as forzenset object. You can check what fields changed like this:

    @receiver(post_save, sender=Mode)
    def post_save(sender, instance, created, **kwargs):
        if not created:
            for item in iter(kwargs.get('update_fields')):
                if item == 'field_name' and instance.field_name == "some_value":
                   # do something here
    

    But there is an issue in this solution. If your field value for example was 10, and you update this field with 10 again, this field will be in update_fields again.

    0 讨论(0)
提交回复
热议问题