Django REST 修改用户密码视图
我正在使用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 个回答
你的问题可以在这里找到答案:https://stackoverflow.com/a/27586192/2950621
在视图类里不要处理密码。应该在 UserSerializer
类的 create
和 update
方法中添加 set_password
的调用。
你可以使用 ModelViewSet
来创建你的 patch
(部分更新)视图,像这样:
class UserViewSet(viewsets.ModelViewSet):
lookup_field = 'username'
queryset = User.objects.all()
serializer_class = serializers.UserSerializer
根据 @DRC 在引用的答案中所说,在 UserSerializer
类上添加 create
和 update
方法:
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"
}
当我在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)
使用视图集重置密码
在视图中
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')
一种方法是重写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中已经被弃用了。