[Fixed]-How to make a field editable on create and read-only on update in Django REST framework

13👍

Overwrite the update method in the serializer and remove the field:

class MySerializer(serializers.ModelSerializer):        
    def update(self, instance, validated_data):
        validated_data.pop('myfield', None)  # prevent myfield from being updated
        return super().update(instance, validated_data)
👤F.M.F.

2👍

Both above solutions work, but a nice way to create an extensible method for controlling the modify permissions of any CRUD operation is the following:

  1. Create a base serializer class: BaseSerializer
  2. Inherit from this class in all your serializer classes

Control in the base serializer class:

class BaseSerializer(DynamicFieldsSerializer):
    # This overrides a built-in base class method
    def get_extra_kwargs(self):
        """Add additional constraints between CRUD methods to
        any particular field

        NB: Use the same extra_kwags dict object in all method calls
        - important to make changes on central object
        """
        extra_kwargs_for_edit = super().get_extra_kwargs()
        
        # Example of making uuid only editable on create, not update 
        self.add_create_only_constraint_to_field("uuid", extra_kwargs_for_edit)
        return extra_kwargs_for_edit

    def add_create_only_constraint_to_field(self, key: str, extra_kwargs: dict) -> dict:
        """Ensures key is only writable on create, not update"""
        action = self.context["view"].action
        if action in ["create"]:
            kwargs = extra_kwargs.get(key, {})
            kwargs["read_only"] = False
            extra_kwargs[key] = kwargs
        elif action in ["update", "partial_update"]:
            kwargs = extra_kwargs.get(key, {})
            kwargs["read_only"] = True
            extra_kwargs[key] = kwargs

     # You could add other constraints to CRUD operations here
     # def another_field_constrained_by_crud_method(self, key: str, extra_kwargs: dict) -> dict:

class SomeModelSerializer(BaseSerializer):
    # Your serializer logic here
    pass

Thanks to Nicholas Coles for the answer!

👤Peza

0👍

I like to check self.instance to determine if the serializer is in "create mode" or "update mode". You could also check self.partial if you want to allow editing the field for non-partial updates.

class MySerializer(Serializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        if self.instance:
            self.fields['create_only_field'].read_only = True

Dynamically modifying the fields from __init__ seems to be officially endorsed by the docs.

👤Ryan

Leave a comment