Home > Enterprise >  What is the proper way to change custom user password?
What is the proper way to change custom user password?

Time:08-06

I am working on my Django (DRF) application. A have a CustomUser model

class CustomAccountManager(BaseUserManager):
    def create_superuser(self, email, user_name, password, **other_fields):
        ...
        
    def create_user(self, email, user_name, password, **other_fields):

        if not email:
            raise ValueError(_('You must provide an email address'))

        email = self.normalize_email(email)
        user = self.model(email=email, user_name=user_name, **other_fields)
        user.set_password(password)
        user.save()
        return user

class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(_('email address'), unique=True)
    user_name = models.CharField(max_length=150, unique=True) # Full name
    phone_number = models.CharField(max_length=20, unique=True)
    ...

I have created a custom way to change password. I am sending current_password, new_password_verify and new_password_verify in body parameter.

My solution is working, but looks bulky What is the proper way to implement password change in Django?

class CustomUserViewSet(viewsets.ModelViewSet):
    def update(self, request: Request, *args, **kwargs):
        instance: CustomUser = self.get_object()

        serializer = self.get_serializer(
            instance, data=request.data, partial=True
        )
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
        if getattr(instance, "_prefetched_objects_cache", None):
            instance._prefetched_objects_cache = {}
        return Response({
            "is_password_updated": self.update_password(request, instance), # <-------UPDATE
            "result": serializer.data
        }) 


    def update_password(self, request, instance):
        """update password if 'new_password_verify' and 'new_password' are in request"""
        if  "current_password" in request.data and instance.check_password(request.data["current_password"]) and \
            "new_password" in request.data and "new_password_verify" in request.data and \
            request.data["new_password"] == request.data["new_password_verify"]:

            instance.set_password(request.data["new_password"])
            instance.save()
            return True
        return False

CodePudding user response:

I think you can extract the validating logic from the view to make it more modularized by defining the serializer.

class ChangePasswordSerializer(serializers.Serializer):
    current_password = serializers.CharField(trim_whitespace = False, validators=[validate_password]),
    new_password = serializers.CharField(trim_whitespace = False, validators=[validate_password])
    new_password_verify = serializers.CharField(trim_whitespace = False)

    def validate(self, attrs):
        if attrs.get('new_password') != attrs.get('new_password_verify'):
            serializers.ValidationError('Password and confirm password do not match')
        return attrs


def validate_password(value):
    # you can set your own validating logic here if you want to
    # for example, like validations for length or regex 
    pass

Of course, you don't need to upload new_password_verify data, and check that part in the frontend then the new_password_verify field and validate method is not necessary and the code will be simpler.

class CustomUserViewSet(viewsets.ModelViewSet):
    def update(self, request: Request, *args, **kwargs):
        ...
        return Response({
            "is_password_updated": self.update_password(request, instance)
            "result": serializer.data
        }) 


    def update_password(self, request, instance):
        serializer = ChangePasswordSerializer(data = request.data)
        if serializer.is_valid():
            input_data = serializer.validated_data
            cur_password = input_data.get('current_password')
            new_password = input_data.get('new_password')
            if instance.check_password(cur_password):
                instance.set_password(new_password)
                instance.save()
                return True
        return False
  • Related