Django Rest Framework – creating users

First, I create a new application called users.

python manage.py startapp users

In the models.py file, I create a custom user that inherits from the AbstractUser class. I’m adding a role checkbox so that the user will be either a principal or a contractor (client or contractor).

from django.contrib.auth.models import AbstractUser
from django.db import models


class ContractUser(AbstractUser):
    class RoleChoices(models.TextChoices):
        CLIENT = "client"
        CONTRACTOR = "contractor"

    role = models.CharField(
        max_length=10,
        choices=RoleChoices.choices,
        default=RoleChoices.CLIENT
    )

    def __str__(self):
        return self.username

The new user model should be registered in the project’s settings.py file, i.e.

AUTH_USER_MODEL = "users.ContractUser"

In the users application, I create a serializers.py file containing the serializer of the new user model, i.e.

from django.contrib.auth.hashers import make_password
from rest_framework import serializers
from .models import ContractUser


class ContractUserSerializer(serializers.ModelSerializer):
    password = serializers.CharField(
        min_length=8,
        write_only=True,
        style={"input_type": "password"}
    )

    class Meta:
        model = ContractUser
        fields = [
             "id",
             "username",
             "password",
             "email",
             "role"
        ]

    def create(self, validated_data):
        validated_data["password"] = make_password(validated_data["password"])
        return super().create(validated_data)

The serializer includes a CharField to enter a password. This field contains the write_only parameter set to True to allow only writing and not reading of this field. The fields field of the Meta internal class contains a list of all available serializable fields. The create() method creates a password based on user input using the make_password() function.

In the urls.py file of the users application, I define an endpoint, i.e.

from rest_framework.routers import DefaultRouter
from .views import ContractUserCreateRetrieveViewSet

router = DefaultRouter()

router.register("", ContractUserCreateRetrieveViewSet, basename="user")

urlpatterns = router.urls

I attach the endpoint for the users application to the main urls.py file of the project:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/user/", include("users.urls")),
]

The /api/user/ endpoint will allow you to create a user and view data for a given user. To do this, I create a ContractUserCreateRetrieveViewSet() class in the views.py file. To limit the number of available methods, I do not use the ModelViewSet class, but inherit from the GenericViewSet class and the appropriate Mixin classes.

from django.shortcuts import get_object_or_404
from rest_framework import viewsets, mixins
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from .models import ContractUser
from .serializers import ContractUserSerializer

class ContractUserCreateRetrieveViewSet(
    mixins.CreateModelMixin,
    mixins.RetrieveModelMixin,
    viewsets.GenericViewSet,
):
    serializer_class = ContractUserSerializer
    queryset = ContractUser.objects.all()

    def retrieve(self, request, pk):
        permission_classes = [IsAuthenticated]
        queryset = ContractUser.objects.filter(id=request.user.id)
        user = get_object_or_404(queryset, pk=pk)
        serializer = ContractUserSerializer(user)
        return Response(serializer.data)

    def create(self, request):
        serializer = ContractUserSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

To make the newly created user inactive, I used the pre_save signal. For this purpose, I created a new signals.py file in the users application, i.e.

from django.db.models.signals import pre_save
from .models import ContractUser
from django.dispatch import receiver

@receiver(pre_save, sender=ContractUser)
def create_inactive_user(sender, instance, **kwargs):
    instance.is_active = False

The signal should be imported in the ready() function of the UsersConfig() class from the apps.py file:

from django.apps import AppConfig


class UsersConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "users"

    def ready(self):
        import users.signals

Leave a Reply

Your email address will not be published. Required fields are marked *

− three = two