Search code examples
djangodjango-rest-frameworkdjango-serializer

why the Django rest framework ModelSerializer. create and ModelSerializer.validate not working properly?


I am developing an authentication system for User registration through Django rest framework.

serializers.py

from rest_framework import serializers
from .models import NewEmployeeProfile


class RegistrationSerializers(serializers.ModelSerializer):
    '''
    We need to add the password2, as its not the part of the NewEmployeeProfile model. So, we need to make it manually.
    '''
    password2 = serializers.CharField(style={'input_type: password'}, write_only=True)

    class Meta:
        model = NewEmployeeProfile
        fields = ('email', 'first_name', 'last_name', 'employee_code', 'contact', 'dob', 'password', 'password2')
        extra_kwargs = {
            'password': {'write_only': True}
        }

    def create(self, validated_data):
        """
        before we save the new user, we need to make sure that the password1, and password2 matches. In order to do
        that, we need to override the save() method.
        """

        password = self.validated_data['password']
        password2 = self.validated_data['password2']

        if password != password2:
            raise serializers.ValidationError({'password': f'password must match..'})
        return NewEmployeeProfile.objects.create(**validated_data)

    def validate(self, attrs):
        if attrs:
            account = NewEmployeeProfile(
                email=self.validated_data['email'],
                first_name=self.validated_data['first name'],
                last_name=self.validated_data['last name'],
                employee_code=self.validated_data['employee code'],
                contact=self.validated_data['contact'],
                dob=self.validated_data['dob'],
            )
            account.save()
            return account

views.py

class UserRegisterView(ListCreateAPIView):
    create_queryset = NewEmployeeProfile.objects.all()
    serializer_class = RegistrationSerializers(create_queryset, many=True)
    permission_classes = [AllowAny]

    def post(self, request, *args, **kwargs):
        serializer = RegistrationSerializers(data=request.data)
        if serializer.is_valid():
            newUser = serializer.save()
            serializer = RegistrationSerializers(newUser)
            return Response(data={"status": "OK", "message": serializer.data}, status=status.HTTP_201_CREATED)
        return Response(data={"status": "error"}, status=status.HTTP_400_BAD_REQUEST)

models.py

from django.db import models
from django.contrib.auth.base_user import BaseUserManager
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, AbstractUser
from django.utils.translation import gettext_lazy as _


class AccountManager(BaseUserManager):
    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError(_('Superuser must have is_staff=True.'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(_('Superuser must have is_superuser=True.'))
        return self.create_user(email, password, **extra_fields)

    def create_user(self, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        if not email:
            raise ValueError(_('Enter the email before proceeding'))

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


class NewEmployeeProfile(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    employee_code = models.CharField(max_length=10, null=True)
    contact = models.CharField(max_length=10, null=True)
    dob = models.DateField(blank=True, null=True)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )

    objects = AccountManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['first_name', 'last_name', 'contact']

    def __str__(self):
        return str(self.email)

The problem is when I test the API in postman, it gives below output:

{
    "first_name": [
        "This field is required."
    ],
    "last_name": [
        "This field is required."
    ]
}

However, while testing POST method in POSTMAN, I have entered all data.

screenshot:

enter image description here

while troubleshooting I found that in serializers.py inside the "create" function I returned nothing. So, I added

return NewEmployeeProfile.objects.create(**validated_data)

Now when I test my API, the POST submition goes in to infinite loop, giving no output in the POSTMAN 'body'.

I guess, I haven't overridden the "create" and "validate" methods properly.

app.urls
from django.urls import path
from .views import signupView, UserRegisterView

urlpatterns = [
    path('signup/', signupView, name='signup'),
    path('register/', UserRegisterView.as_view(), name='register'),
    ]

project.url

urlpatterns = [
    path('admin/', admin.site.urls),
    path('apii/', include('AUTHENTICATION.urls')),
]

new error:

Bad Request: /apii/register/
[05/Apr/2021 11:17:48] "POST /apii/register/ HTTP/1.1" 400 82

Solution

  • I think issue is with the keys you sent in request in the postman

    I believe it should be first_name and last_name instead of first name and last name respectively.