Why isn't my Django User Model's Password Hashed?

后端 未结 4 586
野趣味
野趣味 2020-12-05 15:06

I am using the Django REST Framework (DRF) to create an endpoint with which I can register new users. However, when I hit the creation endpoint with a POST, the new user is

相关标签:
4条回答
  • 2020-12-05 15:38

    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
    
    0 讨论(0)
  • 2020-12-05 15:39

    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__" 
    
    0 讨论(0)
  • 2020-12-05 15:56

    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:

    1. we user self.Meta.model, so if the model is changed on the serializer, it still works (as long as it has a set_password method of course).
    2. we iterate on validated_data items and not the fields, to account for optionally excludeed 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.

    0 讨论(0)
  • 2020-12-05 15:58

    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
    
    0 讨论(0)
提交回复
热议问题