Skip to content

Instantly share code, notes, and snippets.

@gengue
Last active February 2, 2023 03:50
Show Gist options
  • Save gengue/a914bc1540383bfa5e1374db3df10efd to your computer and use it in GitHub Desktop.
Save gengue/a914bc1540383bfa5e1374db3df10efd to your computer and use it in GitHub Desktop.
Django Rest Framework user endpoint with change password
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from rest_framework import serializers
from .models import User
class UserSerializer(serializers.ModelSerializer):
"""
User accounts serializer
"""
class Meta:
model = User
fields = ('id', 'username', 'email', 'first_name', 'last_name',
'is_active','is_staff', 'is_superuser', 'date_joined',)
read_only_fields = ('username', 'auth_token', 'date_joined',)
class PasswordSerializer(serializers.Serializer):
"""
Serializer for password change endpoint.
"""
old_password = serializers.CharField(required=True)
new_password = serializers.CharField(required=True)
from django.conf.urls import url, include
from django.conf import settings
from django.views import defaults
from django.conf.urls.static import static
from django.contrib import admin
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import DefaultRouter
from users.views import UserViewSet
router = DefaultRouter()
router.register(r'users', UserViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/v1/', include(router.urls)),
url(r'^api/v1/docs/', include_docs_urls(title='MY API'))
]
if settings.DEBUG:
# static/media files
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
import debug_toolbar
urlpatterns += [url(r'^__debug__/', include(debug_toolbar.urls))]
# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals
from rest_framework import viewsets, mixins, status
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.decorators import list_route
from rest_framework.views import APIView
from .models import User
from .permissions import IsSuperuserOrIsSelf
from .serializers import UserSerializer, PasswordSerializer
class UserViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
viewsets.GenericViewSet):
"""
list:
Return a list of all the existing users.
read:
Return the given user.
me:
Return authenticated user.
"""
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (IsSuperuserOrIsSelf,)
@list_route(methods=['put'], serializer_class=PasswordSerializer)
def set_password(self, request):
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
if not user.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
user.set_password(serializer.data.get('new_password'))
user.save()
return Response({'status': 'password set'}, status=status.HTTP_200_OK)
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
@shanemgrey
Copy link

Was happy to find this gist on exactly what I'm trying to accomplish. But it's not working for me.

I don't think this is my issue, because I created IsSuperuserOrIsSelf to return True and still the password isn't reset, but could you share your IsSuperuserOrIsSelf class?

I get a 200 response when I submit old and new passwords to the /users/1/ route. But the password doesn't change in the database.

I notice that you use @list_route instead of @detail_route as in the example on the DRF docs. Is this gist working for you as it is written here? Or were there more updates to get it working?

Thank you for any help you might be able to offer.

@shanemgrey
Copy link

Solved:

The key was finding the part of the DRF docs that indicated the route that is created when using this decorator.
http://www.django-rest-framework.org/api-guide/routers/#extra-link-and-actions

The change to my code was

    @detail_route(methods=['post'], permission_classes=[IsSuperuserOrIsSelf], url_path='change-password')
    def set_password(self, request, pk=None):

Thanks again for posting this gist and getting me most of the way there!

@nosarthur
Copy link

user = self.get_object() is missing in views.py::UserViewSet::set_password

@Aubrey-t
Copy link

Anyone has something similar for reset password instead

@tsunday
Copy link

tsunday commented Aug 27, 2018

@KevinPercy
Copy link

I added @nosarthur response to my code and replaced @list_route with @action and it worked!

@VivienGiraud
Copy link

Hi,
Thanks for it!
Could you share the code for IsSuperuserOrIsSelf ? Seems interresting to me.

@gengue
Copy link
Author

gengue commented Dec 16, 2020

Thank you all! this code is from 5 years ago I will try updated soon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment