38π
The issue is DRF will simply set the field values onto the model. Therefore, the password is set on the password field, and saved in the database. But to properly set a password, you need to call the set_password()
method, that will do the hashing.
There are several ways to do this, but the best way on rest framework v3 is to override the update()
and create()
methods on your Serializer.
class UserSerializer(serializers.ModelSerializer):
# <Your other UserSerializer stuff here>
def create(self, validated_data):
password = validated_data.pop('password', None)
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
def update(self, instance, validated_data):
for attr, value in validated_data.items():
if attr == 'password':
instance.set_password(value)
else:
setattr(instance, attr, value)
instance.save()
return instance
Two things here:
- we user
self.Meta.model
, so if the model is changed on the
serializer, it still works (as long as it has aset_password
method of course). - we iterate on
validated_data
items and not
the fields, to account for optionallyexclude
ed fields.
Also, this version of create
does not save M2M relations. Not needed in your example, but it could be added if required. You would need to pop those from the dict, save the model and set them afterwards.
FWIW, I thereby make all python code in this answer public domain worldwide. It is distributed without any warranty.
3π
This worked for me.
class UserSerializer(serializers.ModelSerializer):
def create(self, *args, **kwargs):
user = super().create(*args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
def update(self, *args, **kwargs):
user = super().update(*args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
class Meta:
model = get_user_model()
fields = "__all__"
2π
just override the create and update methods of the serializer:
def create(self, validated_data):
user = get_user_model(**validated_data)
user.set_password(validated_data['password'])
user.save()
return user
def update(self, instance, validated_data):
for f in UserSerializer.Meta.fields + UserSerializer.Meta.write_only_fields:
set_attr(instance, f, validated_data[f])
instance.set_password(validated_data['password'])
instance.save()
return instance
- Using Django's built in web server in a production environment
- What method attributes are used in Django?
- What is the IP address of my heroku application
- TimeField format in Django template
0π
Here is an alternative to accepted answer.
class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email', 'username', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User.objects.create_user(
email=validated_data['email'],
username=validated_data['username'],
password=validated_data['password'],
)
user.save()
return user
create_user
function is defined in UserManager
and it uses set_password()
, we donβt need to use it explicitly. I have found many answers and articles which suggest to use set_password
but after trying many things I figured the above and it works with CustomUserManager
too.
Suppose phone number and password is required to register a user. So our CustomUserManager
will look something like this and CreateUserSerializer
will handle this too with no changes.
class CustomUserManager(BaseUserManager):
def create_user(self, phone_number, password):
if not phone_number:
raise ValueError('Phone Number must be set')
user = self.model(phone_number=phone_number)
user.set_password(password)
user.save(using=self._db)
return user
- Reverse Queryset Order in Django
- Django collectstatic no such file or directory
- Django database synchronization for an offline usage