我正在使用Django REST Framework(DRF)创建一个端点,我可以使用它来注册新用户。但是,当我使用POST命中创建端点时,新用户通过序列化程序保存,但密码以明文形式保存在数据库中。我的序列化程序的代码如下:
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ['password', 'username', 'first_name', 'last_name', 'email']
read_only_fields = ['is_staff', 'is_superuser']
write_only_fields = ['password']
请注意我使用的是Django auth软件包中的默认用户模型,而且我对使用DRF非常新!另外,我发现this question提供了一个解决方案,但这似乎需要两次数据库交互 - 我不认为这是有效的,但这对我来说可能是一个不正确的假设。
答案 0 :(得分:29)
问题是DRF只会将字段值设置到模型上。因此,密码在密码字段中设置,并保存在数据库中。但是要正确设置密码,您需要调用set_password()
方法来进行散列。
有几种方法可以做到这一点,但是休息框架v3的最佳方法是覆盖序列化程序上的update()
和create()
方法。
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
这里有两件事:
self.Meta.model
,因此如果更改了模型
序列化程序,它仍然有效(只要它有一个set_password
当然的方法)。validated_data
项而不是
字段,以考虑可选的exclude
ed字段。此外,此版本的create
无法保存M2M关系。您的示例中不需要,但可以根据需要添加。你需要从dict弹出那些,保存模型并在之后设置它们。
FWIW,因此我在全球这个答案公共领域制作了所有python代码。它的发布没有任何保证。
答案 1 :(得分:2)
只需覆盖序列化程序的create and update methods:
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
答案 2 :(得分:1)
这对我有用。
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__"
答案 3 :(得分:0)
这是公认答案的替代方法。
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
函数在UserManager
中定义,并且使用set_password()
,我们无需显式使用它。我找到了许多建议使用set_password
的答案和文章,但是尝试了许多事情之后,我想到了上面的内容,它也适用于CustomUserManager
。
假设注册用户需要电话号码和密码。因此,我们的CustomUserManager
看起来会像这样,而CreateUserSerializer
也会在不做任何更改的情况下进行处理。
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