7

I am using Django Rest to create a simple API.I need to create a view where the user can change his/hers password.I am using the default Django user model and a simple UserSerializer. There is method called set_password but i cant find a way the use it corrently with the user seriliazer.I cant find any solution anywhere.

UserSelializer:

class UserSerializer(serializers.ModelSerializer):

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

View(ClassedBased): Here is an example (i have no idea what i am doing here):

    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)

Mind that i want to post a json script to change to password. Something like this :

 {
    "old_password": "123", 
    "new_password": "12345"
}
mariodev
  • 13,928
  • 3
  • 49
  • 61
user3418042
  • 179
  • 1
  • 2
  • 7

3 Answers3

1

One method could be to override the restore_object method in the Serializer. This would look something like this:

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)

Now, when this deserializes into an object instance, you will have a valid hashed password. You should then be able to accomplish what you desire by modifying the view you currently have by just a bit.

   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)

And I believe the JSON in your PATCH request would (depending on the look up type, which I think defaults to id) look something like this:

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

I hope this helps!


EDIT:

Please note that as Symmetric has pointed out in the comments, restore_object has been deprecated in DRF 3.0

jnishiyama
  • 377
  • 2
  • 8
  • Note that restore_object is deprecated in DRF 3.0, and it has been replaced with create() and update() methods; see http://www.django-rest-framework.org/topics/3.0-announcement/#the-create-and-update-methods – Symmetric Jan 18 '16 at 21:18
  • Totally right, I will edit to make it more explicit. – jnishiyama Jan 18 '16 at 22:28
1

Password reset using Viewset

In View

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)

You can make a serializer only for password in 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')
Vinay Kumar
  • 1,199
  • 13
  • 16
0

Your question is answered at: https://stackoverflow.com/a/27586192/2950621

Don't do anything about passwords in the view class. Add set_password calls in overridden create and update methods on the UserSerializer class.

Your patch (partial update) view can be created using ModelViewSet like:

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

As per @DRC in the cited answer, add create and update methods on the UserSerializer class:

   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

Also, your incoming JSON should look more like:

{
    "username": "mariodev", 
    "password": "12345"
}
Community
  • 1
  • 1
nmgeek
  • 2,127
  • 1
  • 23
  • 31