Python 如何在 Django Rest Framework 中更新用户密码?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/38845051/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to update user password in Django Rest Framework?
提问by bysucpmeti
I want to ask that following code provides updating password but I want to update password after current password confirmation process. So what should I add for it? Thank you.
我想问以下代码提供更新密码,但我想在当前密码确认过程后更新密码。那么我应该为它添加什么?谢谢你。
class UserPasswordSerializer(ModelSerializer):
class Meta:
model = User
fields = [
'password'
]
extra_kwargs = {
"password": {"write_only": True},
}
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
回答by Yi?it Güler
I believe that using a modelserializer might be an overkill. This simple serializer & view should work.
我相信使用模型序列化器可能是一种矫枉过正。这个简单的序列化器和视图应该可以工作。
Serializers.py
序列化器.py
from rest_framework import serializers
from django.contrib.auth.models import User
class ChangePasswordSerializer(serializers.Serializer):
model = User
"""
Serializer for password change endpoint.
"""
old_password = serializers.CharField(required=True)
new_password = serializers.CharField(required=True)
Views.py
视图.py
from rest_framework import status
from rest_framework import generics
from rest_framework.response import Response
from django.contrib.auth.models import User
from . import serializers
from rest_framework.permissions import IsAuthenticated
class ChangePasswordView(UpdateAPIView):
"""
An endpoint for changing password.
"""
serializer_class = ChangePasswordSerializer
model = User
permission_classes = (IsAuthenticated,)
def get_object(self, queryset=None):
obj = self.request.user
return obj
def update(self, request, *args, **kwargs):
self.object = self.get_object()
serializer = self.get_serializer(data=request.data)
if serializer.is_valid():
# Check old password
if not self.object.check_password(serializer.data.get("old_password")):
return Response({"old_password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST)
# set_password also hashes the password that the user will get
self.object.set_password(serializer.data.get("new_password"))
self.object.save()
response = {
'status': 'success',
'code': status.HTTP_200_OK,
'message': 'Password updated successfully',
'data': []
}
return Response(response)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
回答by tominardi
@Yi?it Güler give a good answer, thanks, but it could be better in some minor points.
@Yi?it Güler 给出了一个很好的答案,谢谢,但在一些小问题上可能会更好。
As long you don't really works with UpdateModelMixin, but directly with the request user instance, you don't need to use a UpdateAPIView. A simple APIViewis enough.
只要您不真正使用UpdateModelMixin,而是直接使用请求用户实例,您就不需要使用UpdateAPIView。一个简单的APIView就足够了。
Also, when the password is changed, you can return a status.HTTP_204_NO_CONTENT
instead of a 200 with some random content.
此外,当密码更改时,您可以返回 astatus.HTTP_204_NO_CONTENT
而不是 200 带有一些随机内容。
By the way, don't forgot to validate your new password before save. It's too bad if you allow "password" at update while you don't at create.
顺便说一句,在保存之前不要忘记验证您的新密码。如果您在不创建时允许在更新时使用“密码”,那就太糟糕了。
So I use the following code in my project:
所以我在我的项目中使用了以下代码:
from django.contrib.auth.password_validation import validate_password
class ChangePasswordSerializer(serializers.Serializer):
"""
Serializer for password change endpoint.
"""
old_password = serializers.CharField(required=True)
new_password = serializers.CharField(required=True)
def validate_new_password(self, value):
validate_password(value)
return value
And for the view:
对于视图:
class UpdatePassword(APIView):
"""
An endpoint for changing password.
"""
permission_classes = (permissions.IsAuthenticated, )
def get_object(self, queryset=None):
return self.request.user
def put(self, request, *args, **kwargs):
self.object = self.get_object()
serializer = ChangePasswordSerializer(data=request.data)
if serializer.is_valid():
# Check old password
old_password = serializer.data.get("old_password")
if not self.object.check_password(old_password):
return Response({"old_password": ["Wrong password."]},
status=status.HTTP_400_BAD_REQUEST)
# set_password also hashes the password that the user will get
self.object.set_password(serializer.data.get("new_password"))
self.object.save()
return Response(status=status.HTTP_204_NO_CONTENT)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
回答by Michael van de Waeter
After you save the user, you might want to make sure that the user stays logged in (after django==1.7 an user automatically is logged out on password change):
保存用户后,您可能希望确保用户保持登录状态(在 django==1.7 之后,用户会在密码更改时自动注销):
from django.contrib.auth import update_session_auth_hash
# make sure the user stays logged in
update_session_auth_hash(request, self.object)
回答by Pedro
I dont' think the validation should be done by the view as @Yi?it Güler proposes. Here is my solution:
我不认为验证应该由@Yi?it Güler 提议的视图完成。这是我的解决方案:
serializers.py
序列化程序.py
from django.contrib.auth import password_validation
from rest_framework import serializers
class ChangePasswordSerializer(serializers.Serializer):
old_password = serializers.CharField(max_length=128, write_only=True, required=True)
new_password1 = serializers.CharField(max_length=128, write_only=True, required=True)
new_password2 = serializers.CharField(max_length=128, write_only=True, required=True)
def validate_old_password(self, value):
user = self.context['request'].user
if not user.check_password(value):
raise serializers.ValidationError(
_('Your old password was entered incorrectly. Please enter it again.')
)
return value
def validate(self, data):
if data['new_password1'] != data['new_password2']:
raise serializers.ValidationError({'new_password2': _("The two password fields didn't match.")})
password_validation.validate_password(data['new_password1'], self.context['request'].user)
return data
def save(self, **kwargs):
password = self.validated_data['new_password1']
user = self.context['request'].user
user.set_password(password)
user.save()
return user
view.py
查看.py
from rest_framework.generics import UpdateAPIView
from rest_framework.authtoken.models import Token
class ChangePasswordView(UpdateAPIView):
serializer_class = ChangePasswordSerializer
def update(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
# if using drf authtoken, create a new token
if hasattr(user, 'auth_token'):
user.auth_token.delete()
token, created = Token.objects.get_or_create(user=user)
# return new token
return Response({'token': token.key}, status=status.HTTP_200_OK)
回答by danielrosheuvel
So I decided to override the update function within ModelSerializer. Then get the password of the User instance. Afterwards run the necessary comparisons of making sure old password is the same as the one currently on the user instance via the check_password function and making sure new password and confirm password slot values are the same then proceed to set the new password if true and save the instance and return it.
所以我决定覆盖 ModelSerializer 中的更新功能。然后获取 User 实例的密码。然后通过 check_password 函数运行必要的比较以确保旧密码与当前用户实例上的相同,并确保新密码和确认密码槽值相同,然后继续设置新密码(如果为真)并保存实例并返回它。
serializers.py
序列化程序.py
class ChangePasswordSerializer(ModelSerializer):
confirm_password = CharField(write_only=True)
new_password = CharField(write_only=True)
old_password = CharField(write_only=True)
class Meta:
model = User
fields = ['id', 'username', 'password', 'old_password', 'new_password','confirm_password']
def update(self, instance, validated_data):
instance.password = validated_data.get('password', instance.password)
if not validated_data['new_password']:
raise serializers.ValidationError({'new_password': 'not found'})
if not validated_data['old_password']:
raise serializers.ValidationError({'old_password': 'not found'})
if not instance.check_password(validated_data['old_password']):
raise serializers.ValidationError({'old_password': 'wrong password'})
if validated_data['new_password'] != validated_data['confirm_password']:
raise serializers.ValidationError({'passwords': 'passwords do not match'})
if validated_data['new_password'] == validated_data['confirm_password'] and instance.check_password(validated_data['old_password']):
# instance.password = validated_data['new_password']
print(instance.password)
instance.set_password(validated_data['new_password'])
print(instance.password)
instance.save()
return instance
return instance
views.py
视图.py
class ChangePasswordView(RetrieveUpdateAPIView):
queryset= User.objects.all()
serializer_class = ChangePasswordSerializer
permission_classes = [IsAuthenticated]
回答by Igor
I think the easiest (when I say easiest, I mean shortest possible and cleaner) solution would be something like:
我认为最简单的(当我说最简单时,我的意思是尽可能短和更干净)的解决方案是这样的:
View class
查看班级
class APIChangePasswordView(UpdateAPIView):
serializer_class = UserPasswordChangeSerializer
model = get_user_model() # your user model
permission_classes = (IsAuthenticated,)
def get_object(self, queryset=None):
return self.request.user
Serializer class
序列化器类
from rest_framework import serializers
from rest_framework.serializers import Serializer
class UserPasswordChangeSerializer(Serializer):
old_password = serializers.CharField(required=True, max_length=30)
password = serializers.CharField(required=True, max_length=30)
confirmed_password = serializers.CharField(required=True, max_length=30)
def validate(self, data):
# add here additional check for password strength if needed
if not self.context['request'].user.check_password(data.get('old_password')):
raise serializers.ValidationError({'old_password': 'Wrong password.'})
if data.get('confirmed_password') != data.get('password'):
raise serializers.ValidationError({'password': 'Password must be confirmed correctly.'})
return data
def update(self, instance, validated_data):
instance.set_password(validated_data['password'])
instance.save()
return instance
def create(self, validated_data):
pass
@property
def data(self):
# just return success dictionary. you can change this to your need, but i dont think output should be user data after password change
return {'Success': True}
回答by Anar Ali
serializer.py
序列化程序.py
class UserSer(serializers.ModelSerializers):
class meta:
model=UserModel
fields = '__all__'
views.py
视图.py
class UserView(UpdateAPIView):
serializer_class = serializers.UserSer
queryset = models.User.objects.all()
def get_object(self,pk):
try:
return models.User.objects.get(pk=pk)
except Exception as e:
return Response({'message':str(e)})
def put(self,request,pk,format=None):
user = self.get_object(pk)
serializer = self.serializer_class(user,data=request.data)
if serializer.is_valid():
serializer.save()
user.set_password(serializer.data.get('password'))
user.save()
return Response(serializer.data)
return Response({'message':True})