See object changes in post_save in django rest fra

2019-02-15 02:24发布

问题:

I am curious if there is a way to see what has changed on an object after saving it using the Django Rest Framework. I have some special behavior I need to check if a field has been changed from its original value that I was hoping to handle using the post_save on generics.RetrieveUpdateDestroyAPIView.

My first thought was to check using pre_save but it seems that pre_save's object argument already has the changes applied to it.

回答1:

OLD ANSWER for django rest framework version 2.3.12:

To check if anything has changed on update, you will have to compare the unchanged model instance which is self.object with the changed model instance which is serializer.object.

The object argument which is passed to the pre_save method is the serializer.object which is not yet saved in the database with the new changes.

The unchanged model instance is the self.object which has been fetched from the database using self.get_object_or_none(). Compare it with the obj argument in the pre_save method.

def pre_save(self,obj):
    unchanged_instance = self.object
    changed_instance = obj
    ..... # comparison code

NEW ANSWER for django rest framework 3.3:

pre_save and post_save are no longer valid http://www.django-rest-framework.org/topics/3.0-announcement/#generic-views

Now you can place any pre save or post save logic in perform_update method. For example:

def perform_update(self, serializer):
    old_obj = self.get_object()
    new_data_dict = serializer.validated_data
    # pre save logic
    if old_obj.name != new_data_dict['name']
        do_smething
    .....
    serializer.save()
    # post save logic
    ......


回答2:

I was able to do this with help from model_utils FieldTracker. You can install a tracker on the relevant model, then in pre_save (by post_save it's too late) you can do this:

def pre_save(self, obj):
    if hasattr(obj, 'tracker'):
        self.changed_fields = obj.tracker.changed()
    else:
        self.changed_fields = None

changed_fields will look like this: {'is_public': False, 'desc': None}