Reputation: 11656
In my project I want to set password as both read_only
(because I have a separate endpoint to reset password) and write_only
(because I don't want password send in the response).
Here is my serializer:
class UserSerializer(serializers.ModelSerializer):
"""A Serizlier class for User """
class Meta:
model = models.User
fields = ('id', 'email', 'phone_number', 'user_type', 'password')
extra_kwargs = { 'password': { 'write_only': True} }
read_only_fields = ('password',)
But I get an error saying:
AssertionError at /api/user/21/
May not set both
read_only
andwrite_only
How can I have a field be both read_only
and write_only
?
Upvotes: 3
Views: 5415
Reputation: 23
You can check the type of method as well:
class UserSerializer(serializers.ModelSerializer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if request and request.method in ['POST', 'PUT', 'PATCH']:
self.fields.get('password').write_only = True
else:
self.fields.get('password').read_only = True
Upvotes: 0
Reputation: 1195
So extending on the answer of @JPG
The Serializer
class ProfileSerializer(serializers.ModelSerializer):
name = serializers.CharField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if 'create_method' in self.context:
self.fields['name'].write_only = True
else:
self.fields['name'].read_only = True
And the view set
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
def get_serializer_context(self):
# Call Parent
context = super().get_serializer_context()
# Check If This Is a POST
if self.request.method == 'POST':
context['create_method'] = True
# Handoff
return context
This will allow name to be written on POST and read only on everything else
Upvotes: 2
Reputation: 1299
well - normally if you have two endpoints using a similar serializer which needs to differ only with a certain field/functionality you would create a base class and abstract it and only change/modify the parts of it that need to change. Here is what I would do.
class (serializers.ModelSerializer):
"""A Serizlier class for User """
class Meta:
model = models.User
fields = ('id', 'email', 'phone_number', 'user_type', 'password')
extra_kwargs = { 'password': { 'read_only': True} }
class UserSerializerForOtherView(UserSerializer):
class Meta(UserSerializer.Meta):
extra_kwargs = { 'password': { 'write_only': True} }
Now UserSerializerForOtherView
inherits the same behaviour as UserSerializer
and you now also have a new serializer should you want to further expand on functionality on this serializer alone.
All you will need to do is tell the other view to use the other serializer.
Upvotes: 0
Reputation: 88689
Override the __init__()
method of the serializer as,
class UserSerializer(serializers.ModelSerializer):
"""A Serizlier class for User """
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.context['some_flag']:
self.fields['password'].read_only = True
else:
self.fields['password'].write_only = True
class Meta:
model = models.User
fields = ('id', 'email', 'phone_number', 'user_type', 'password')
# extra_kwargs = { 'password': { 'write_only': True} } # remove this
# read_only_fields = ('password',) # remove this
The some_flag
variable is something that you should pass to the serializer either from the password reset view or from the other view
Upvotes: 3