Reputation: 3880
Right now, DRF's read_only
argument on a Serializer
constructor means you can neither create nor update the field, while the write_only
argument on a Serializer
constructor allows the field to be created OR updated, but prevents the field from being output when serializing the representation.
Is there any (elegant) way to have a Serializer
field that can be created, exactly once, when the model in question is created (when the create()
is called on the Serializer
), but cannot that later be modified via update
?
NB: Yes, I've seen this solution, but honestly I find it ugly and un-Pythonic. Is there a better way?
Upvotes: 8
Views: 4463
Reputation: 125
In order to make the field REQUIRED and read-only on update I've handled it on field validation.
class MyUserProfileSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username', required=True)
first_name = serializers.CharField(source='user.first_name')
last_name = serializers.CharField(source='user.last_name')
class Meta:
model = UserProfile
fields = ['user', 'username', 'first_name', 'last_name', 'phone']
read_only_fields = []
def validate_username(self, value):
if not self.instance and not value: ## Creation and value not provided
raise serializers.ValidationError('The username is required on user profile creation.')
elif value and self.instance != value: ## Update and value differs from existing
raise serializers.ValidationError('The username cannot be modified.')
return value
You can also make the field optional in case of edition, but you need to set the field as required=False and do validation in validate() method, since validate_username() wouldn't be called in creation if not included in the payload.
Upvotes: 0
Reputation: 7717
class TodoModifySerializer(ModelSerializer):
def to_internal_value(self, data):
data = super(TodoModifySerializer, self).to_internal_value(data)
if self.instance:
# update
for x in self.create_only_fields:
data.pop(x)
return data
class Meta:
model = Todo
fields = ('id', 'category', 'title', 'content')
create_only_fields = ('title',)
you can do it in to_internal_value
method by remove this data when update
Upvotes: 8
Reputation: 12990
By "not elegant", I'm assuming you only want one serializer for both creates and updates. You could perhaps consider overriding the update
method of your serializer and remove the create_only_field
from validated_data
before saving:
class MySerializer(serializers.ModelSerializer):
def update(self, instance, validated_data):
validated_data.pop('create_only_field')
return super().update(instance, validated_data)
class Meta:
model = MyModel
fields = ('id', 'field_one', 'field_two', 'create_only_field')
You would, however, have to supply the old (or some) field value when updating your model.
Upvotes: 4
Reputation: 97
I don't think there's any, you either specify it like that or make your own serializer, inheriting from DRF's serializer.
Upvotes: 1