Django REST 修改用户密码视图

7 投票
4 回答
12233 浏览
提问于 2025-04-18 04:07

我正在使用Django Rest来创建一个简单的API。我需要创建一个视图,让用户可以更改他们的密码。我使用的是默认的Django用户模型和一个简单的UserSerializer。有一个叫set_password的方法,但我找不到正确的方法来与用户序列化器一起使用。我在任何地方都找不到解决方案。

UserSerializer:

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('id', "username", 'email', 'first_name', 'last_name', 'password')

视图(基于类):这里有一个例子(我对这里的操作完全不懂):

    class UserChangePassword(APIView):

        def patch(self, request):
            user = self.request.user
            serialized = UserSerializer(data=request.DATA)
            if serialized.is_valid():
                user.set_password(serialized.data['password'])
                user.save()
                return Response(status=status.HTTP_205_RESET_CONTENT)
            else:
            return Response(serialized.errors, status=status.HTTP_400_BAD_REQUEST)

请注意,我想发送一个JSON脚本来更改密码。大概是这样的:

 {
    "old_password": "123", 
    "new_password": "12345"
}

4 个回答

0

你的问题可以在这里找到答案:https://stackoverflow.com/a/27586192/2950621

在视图类里不要处理密码。应该在 UserSerializer 类的 createupdate 方法中添加 set_password 的调用。

你可以使用 ModelViewSet 来创建你的 patch(部分更新)视图,像这样:

class UserViewSet(viewsets.ModelViewSet):
    lookup_field = 'username'
    queryset = User.objects.all()
    serializer_class = serializers.UserSerializer

根据 @DRC 在引用的答案中所说,在 UserSerializer 类上添加 createupdate 方法:

   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

另外,你接收到的 JSON 数据应该更像这样:

{
    "username": "mariodev", 
    "password": "12345"
}
1

当我在APIView中尝试使用make_password时,遇到了这个错误

raise AttributeError("This QueryDict instance is immutable")
AttributeError: This QueryDict instance is immutable

class UserSerializer(serializers.ModelSerializer):
    resume = serializers.CharField(source='userprofile.resume')
    password = serializers.CharField(write_only=True)
    class Meta:
        model = User
        fields =  ('first_name', 'last_name', 'email', 'username', 'resume', 'password')

class UpdateUser(APIView):

    permission_classes = [IsAuthenticated]

    def patch(self, request):
        user = User.objects.get(username=request.user)
        if 'password' in request.data and request.data['password'] != '':
            request.data['password'] = make_password(request.data['password'])
        serializer = UserSerializer(user, data=request.data, many=False, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response({'message': "Error updated"}, status=status.HTTP_400_BAD_REQUEST)

我通过执行mutable_data = request.data.copy()来解决了这个问题,我想问的是,这样做可以吗?还是我做错了什么,或者这是不好的做法?

class UpdateUser(APIView):

    permission_classes = [IsAuthenticated]

    def patch(self, request):
        user = User.objects.get(username=request.user)
        mutable_data = request.data.copy()
        if 'password' in mutable_data and mutable_data['password'] != '':
            mutable_data['password'] = make_password(mutable_data['password'])
        serializer = UserSerializer(user, data=mutable_data, many=False, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response({'message': "Error updated"}, status=status.HTTP_400_BAD_REQUEST)
1

使用视图集重置密码

在视图中

from rest_framework.decorators import detail_route, list_route, permission_classes
from rest_framework import viewsets
class UserProfileViewSet(viewsets.ViewSet):

    permission_classes = (AllowAny,)
    serializer_class = UserProfileSerializer

    def list(self, request):
        queryset = UserProfile.objects.all()
        serializer = self.serializer_class(queryset, many=True)
        return Response(serializer.data)

    def create(self, request):
        serializer = self.serializer_class(data=request.data)
        # check email address is exists or not.
        user_type = request.data['user_type']
        user_token = register_by_social(request.data['email'], request.data['username'], user_type)

        if not user_token or user_token == True:
            if not User.objects.filter(Q(email=request.data['email']) 
                | Q(username=request.data['username'])).exists():

                if serializer.is_valid():
                    userprofile = serializer.save()

                    return Response({
                        'status': status.HTTP_201_CREATED,
                        'message': 'Successfully signup new user.',
                        'token': userprofile.user.auth_token.key })

                return Response({
                    'status': status.HTTP_400_BAD_REQUEST,
                    'message': 'Please provided required fields.',
                    'error' : serializer.errors })

            return Response({
                'status': status.HTTP_409_CONFLICT,
                'message': 'Email address or username is already exists.'})

        return Response({
            'status': status.HTTP_200_OK,
            'message': 'Social user is already registered.',
            'token': user_token })

    @list_route(permission_classes=[IsAuthenticated], authentication_classes = (BasicAuthentication, TokenAuthentication), 
            methods=['post'], url_path='reset-user-password')
    def reset_user_password(self, request, pk=None):

        reset_password_serializer = UserResetPasswordSerializer(request.user, data=request.data)

        if reset_password_serializer.is_valid():

            if not request.user.check_password(request.data.get('password')):
                return Response({"password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST)

            request.user.set_password(request.data.get('new_password'))
            request.user.save()
            return Response({"Message": ["Password reset successfully"]}, status=status.HTTP_200_OK)

你可以在serializer.py文件中专门为密码创建一个序列化器

import django.contrib.auth.password_validation as validators
class UserResetPasswordSerializer(serializers.ModelSerializer):
    password = serializers.CharField(source='user.password', style={'input_type': 'password'},
        max_length=20, min_length=8)
    new_password = serializers.CharField(style={'input_type': 'password'},
        max_length=20, min_length=8)
    class Meta:
        model = User
        fields =("password", 'new_password')
1

一种方法是重写Serializer中的restore_object方法。这个方法大概是这样的:

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('id', "username", 'email', 'first_name', 'last_name', 'password')

    # turn text to hashed password
    def restore_object(self, attrs, instance=None):
        attrs['password'] = make_password(attrs['password'])
        return super(UserSerializer, self).restore_object(attrs, instance=None)

现在,当这个方法把数据转回对象实例时,你会得到一个有效的哈希密码。然后,你只需要稍微修改一下你现在的视图,就能实现你想要的功能。

   class UserChangePassword(APIView):

       def patch(self, request):
           serialized = UserSerializer(data=request.DATA)
           if serialized.is_valid():
               serialized.save()
               return Response(status=status.HTTP_205_RESET_CONTENT)
           else:
               return Response(serialized.errors, status=status.HTTP_400_BAD_REQUEST)

我认为你在PATCH请求中的JSON数据会(根据查找类型,我想默认是id)看起来像这样:

{
  "id": "83",
  "password": "12345"
}

希望这对你有帮助!


编辑:

请注意,正如Symmetric在评论中指出的,restore_object在DRF 3.0中已经被弃用了。

撰写回答