Django REST User Creation/Authentication

Viewed 792

This question is based on the one here. I am setting up a Django REST Framework for my web app and am trying to set up User accounts. Based on the REST documentation, they put all of their account code in their example in the main project directory and a separate application so did that as well. Here is what I have:

urls.py

from django.contrib import admin
from django.urls import include, path
from django.conf.urls import url
from rest_framework import routers
from . import views

router = routers.DefaultRouter()
router.register('users', views.UserViewSet)

urlpatterns = [
    path('admin/', admin.site.urls),
    url('', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
]

serializers.py

from django.contrib.auth.models import User
from rest_framework import serializers


class UserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)

    def create(self, validated_data):
        user = User.objects.create(
            username=validated_data['username']
        )
        user.set_password(validated_data['password'])
        user.save()

        return user

    class Meta:
        model = User
        # Tuple of serialized model fields (see link [2])
        fields = ( "id", "username", "password", )

views.py

from rest_framework import viewsets, permissions
from rest_framework.generics import CreateAPIView
from django.contrib.auth.models import User
from .serializers import UserSerializer


# Create your views here.
class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [permissions.IsAuthenticated]


class CreateUserView(CreateAPIView):
    model = User
    permission_classes = [
        permissions.AllowAny
    ]
    serializer_class = UserSerializer

I have tried using the Boomerang REST Client in Chrome to POST data to this API, but it always returns a 403 Error saying "Invalid username/password." Specifically I am POSTing to http://127.0.0.1:8000/users/create/ with a Query String and 2 parameters: username and password. I also tried sending it as JSON and it returned the same. Any help would be appreciated.

3 Answers

It doesn't look like CreateUserView was registered in your urls.py. You should be able to register it and access it normally. I think this should work for you:

urlpatterns = [
    ...
    url(r'^users/create/', views.CreateUserView.as_view()),
]

That said, I'd like to suggest adding an extra action for your UserViewSet instead:

# Create your views here.
class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [permissions.IsAuthenticated

    @action(methods=['post'], detail=False, permission_classes=[permissions.AllowAny])
    def register(self, request, *args, **kwargs):
        # This logic was taken from the `create` on `ModelViewSet`. Alter as needed.
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

Then you should be able to post via /users/register/. You can also specify your own url name and path on the decorator.

based on django-rest-framework documents it's better to use viewset for create user api. therefor you need to send a POST request to http://127.0.0.1:8000/api-auth/users and no need to CreateUserView function.

But if you want to have a custom user create api do you need something like below:

class UserViewSet(viewsets.ModelViewSet):
    """
    A viewset that provides the standard actions
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

    @action(detail=True, methods=['post'], permission_classes=[permissions.AllowAny])
    def create_user(self, request, pk=None):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

To have custom serializers in your ViewSet you can specify them in get_serializer_class function in your ViewSet like this:

class UserViewSet(viewsets.ModelViewSet):
# example viewset
    def get_serializer_class(self):
        if self.action == 'list':
            return ListUserSerializer
        elif self.action == 'create':
            return CreateUserSerializer
        elif self.action == 'update':
            return UpdateUserSerializer
        return DetailUserSerializer
Related