为什么我的Django用户模型没有密码?

时间:2014-12-21 01:59:58

标签: python django django-rest-framework

我正在使用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提供了一个解决方案,但这似乎需要两次数据库交互 - 我不认为这是有效的,但这对我来说可能是一个不正确的假设。

4 个答案:

答案 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

这里有两件事:

  1. 我们是用户self.Meta.model,因此如果更改了模型 序列化程序,它仍然有效(只要它有一个set_password 当然的方法)。
  2. 我们迭代validated_data项而不是 字段,以考虑可选的exclude ed字段。
  3. 此外,此版本的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