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
In order to use the built-in Django check_password() function, we need to import it, which is shown in the first line of code. So the current password of the user is, request. user. password, which we store in the currentpassword variable.
By default, Django uses the PBKDF2 algorithm with a SHA256 hash, a password stretching mechanism recommended by NIST. This should be sufficient for most users: it's quite secure, requiring massive amounts of computing time to break.
I believe that using a modelserializer might be an overkill. This simple serializer & view should work.
Serializers.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
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)
@Yiğit Güler give a good answer, thanks, but it could be better in some minor points.
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 APIView is enough.
Also, when the password is changed, you can return a status.HTTP_204_NO_CONTENT
instead of a 200 with some random content.
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)
I dont' think the validation should be done by the view as @Yiğit Güler proposes. Here is my solution:
serializers.py
from django.contrib.auth import password_validation
from django.utils.translation import gettext_lazy as _
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
views.py
from rest_framework import status
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With