Situation
I am creating a simple endpoint that allows for the creation of a user. I need a field that is not in my user model (i.e., confirm_password
). I will run validation that compares this field and another field that is in my model, and then never use the additional field again in the serializer.
Problem
DRF version 3 has changed the process for accomplishing this, and I do not quite understand what the documentation is suggesting I do. See here for the documentation.
Attempt at Solution
I have created a UserSerializer
that looks like this:
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
confirm_password = serializers.CharField(allow_blank=False)
def validate(self, data):
"""
Checks to be sure that the received password and confirm_password
fields are exactly the same
"""
if data['password'] != data.pop('confirm_password'):
raise serializers.ValidationError("Passwords do not match")
return data
def create(self, validated_data):
"""
Creates the user if validation succeeds
"""
password = validated_data.pop('password', None)
user = self.Meta.model(**validated_data)
user.set_password(password)
user.save()
return user
class Meta:
# returns the proper auth model
model = get_user_model()
# fields that will be deserialized
fields = ['password', 'confirm_password',
'username', 'first_name', 'last_name', 'email']
# fields that will be serialized only
read_only_fields = ['is_staff', 'is_superuser']
# fields that will be deserialized only
write_only_fields = ['password' 'confirm_password']
I was hoping that popping confirm_password
in validate
would take care of my problems, but I just get the following:
Got
KeyError
when attempting to get a value for fieldconfirm_password
on serializerUserSerializer
. The serializer field might be named incorrectly and not match any attribute or key on theOrderedDict
instance
You are looking for a write-only field, as I'm assuming you won't want to display the password confirmation in the API. Django REST Framework introduced the
write_only
parameter in the 2.3.x timeline to complement theread_only
parameter, so the only time validation is run is when an update is being made. Thewrite_only_fields
meta property was added around the same time, but it's important to understand how both of these work together.The
write_only_fields
meta property will automatically add thewrite_only
property to a field when it is automatically created, like for apassword
field on aUser
model. It will not do this for any fields which are not on the model, or fields that have been explicitly specified on the serializer. In your case, you are explicitly specifying theconfirm_password
field on your serializer, which is why it is not working.This is raised during the re-serialization of the created user, when it is trying to serialize your
confirm_password
field. Because it cannot find the field on theUser
model, it triggers this error which tries to explain the problem. Unfortunately because this is on a new user, it tells you to confusingly look at theOrderedDict
instance instead of theUser
instance.If you explicitly specify
write_only
on the serializer field, and remove the field from yourwrite_only_fields
, then you should see the behaviour your are expecting.You can find documentation about this on this link
Also useful for nested serializer implementation representing models, when the root model doesn't have directly access to fields you want to use. Thank you @vyscond ;) Fyi here is my case:
models.py
serializer.py