Django REST Serializer Method Writable Field
You need to use another type of field:
class ClientSerializer(serializers.ModelSerializer):
email = serializers.EmailField(source='user.email')
def create(self, validated_data):
# DRF will create object {"user": {"email": "inputed_value"}} in validated_date
email = validated_data.get("user", {}).get('email')
class Meta:
model = Client
fields = (
"id",
"email",
)
I tried to use Guilherme Nakayama da Silva and Julio Marins's answers to fix my problem with writing to a SerializerMethodField. It worked for reading and updating, but not for creating.
So I created my own WritableSerializerMethodField based on their answers, it works perfectly for reading, creating and writing.
class WritableSerializerMethodField(serializers.SerializerMethodField):
def __init__(self, **kwargs):
self.setter_method_name = kwargs.pop('setter_method_name', None)
self.deserializer_field = kwargs.pop('deserializer_field')
super().__init__(**kwargs)
self.read_only = False
def bind(self, field_name, parent):
retval = super().bind(field_name, parent)
if not self.setter_method_name:
self.setter_method_name = f'set_{field_name}'
return retval
def get_default(self):
default = super().get_default()
return {
self.field_name: default
}
def to_internal_value(self, data):
value = self.deserializer_field.to_internal_value(data)
method = getattr(self.parent, self.setter_method_name)
return {self.field_name: self.deserializer_field.to_internal_value(method(value))}
Then I used this in my serializer
class ProjectSerializer(serializers.ModelSerializer):
contract_price = WritableSerializerMethodField(deserializer_field=serializers.DecimalField(max_digits=12, decimal_places=2))
def get_contract_price(self, project):
return project.contract_price
def set_contract_price(self, value):
return value
Here is a read/write serializer method field:
class ReadWriteSerializerMethodField(serializers.SerializerMethodField):
def __init__(self, method_name=None, **kwargs):
self.method_name = method_name
kwargs['source'] = '*'
super(serializers.SerializerMethodField, self).__init__(**kwargs)
def to_internal_value(self, data):
return {self.field_name: data}