400 Bad Request: Djoser built-in create user using django Signals

44 Views Asked by At

I have 3 models (including Custom User) structured:

accounts.models.py

from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.db import models #type: ignore
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from phonenumber_field.modelfields import PhoneNumberField #type: ignore

# Create your models here.
class UserAccountManager(BaseUserManager):
    def create_user(self, email, user_role, password=None, **extra_fields):
        if not email:
            raise ValueError("Users must have an email address.")

        if not user_role:
            raise ValueError("Users must have a user role.")
        
        email = self.normalize_email(email)
        user = self.model(email=email, user_role=user_role, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user
    
    def create_superuser(self, email, user_role="admin", password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_admin", True)
        extra_fields.setdefault("is_active", True)
        extra_fields.setdefault("is_superuser", True)

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

class UserAccount(AbstractBaseUser, PermissionsMixin):
    USER_ROLES = (
        ("resident", "Resident"),
        ("healthworker", "Health Worker"),
        ("admin", "Admin")
    )

    email = models.EmailField(_("email address"), unique=True)
    user_role = models.CharField(max_length=100, choices=USER_ROLES)
    is_staff = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    date_joined = models.DateTimeField(default=timezone.now)

    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["user_role",]

    objects = UserAccountManager()

    def __str__(self):
        return self.email
    
    def has_perm(self, perm, obj=None):
        return True
    
    def has_module_perms(self, app_label):
        return True
    
    def save(self, *args, **kwargs):
        if self.user_role == "admin":
            self.is_staff = True
            self.is_admin = True
        elif self.user_role == "healthworker":
            self.is_staff = True
            self.is_admin = False
        else:
            self.is_staff = False
            self.is_admin = False
        
        super().save(*args, **kwargs)


class AddressDetails(models.Model):
    address_line_one = models.CharField(max_length=150, blank=True,)
    address_line_two = models.CharField(max_length=150, blank=True,)
    address_line_three = models.CharField(max_length=150, blank=True,)
    province = models.CharField(max_length=150, blank=True,)
    barangay = models.CharField(max_length=150, blank=True,)
    city = models.CharField(max_length=150, blank=True,)
    zip_code = models.CharField(max_length=5, blank=True,)
    
class UserProfile(models.Model):
    GENDER_CHOICES = (
        ("F", "Female",),
        ("M", "Male",),
        ("U", "Unsure",),
    )
    user = models.OneToOneField(UserAccount, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=150,)
    middle_name = models.CharField(max_length=150,)
    last_name = models.CharField(max_length=150,)
    date_of_birth = models.DateField()
    gender = models.CharField(max_length=1, choices=GENDER_CHOICES,)
    relationship_status = models.CharField(max_length=50,)
    phone_number = models.CharField(max_length=50,)
    address_details = models.OneToOneField(AddressDetails, on_delete=models.CASCADE)

   

I tried using POSTMAN to test the /users/ endpoint, In the requesting body contains all the fields from the 3 models. It causes a 400 Bad Request.

accounts.serializers.py

from djoser.serializers import UserCreateSerializer as BaseUserCreateSerializer
from django.contrib.auth import get_user_model
from rest_framework import serializers
# from django.db import transaction
# from django.db.utils import IntegrityError
# from django.contrib.auth.password_validation import validate_password
from .models import AddressDetails, UserProfile

User = get_user_model()

class UserCreateSerializer(BaseUserCreateSerializer):
    address_line_one = serializers.CharField(write_only=True)
    address_line_two = serializers.CharField(write_only=True)
    address_line_three = serializers.CharField(write_only=True)
    province = serializers.CharField(write_only=True, required=True)
    barangay = serializers.CharField(write_only=True, required=True)
    city = serializers.CharField(write_only=True, required=True)
    zip_code = serializers.CharField(write_only=True, required=True)
    first_name = serializers.CharField(write_only=True, required=True)
    middle_name = serializers.CharField(write_only=True, required=True)
    last_name = serializers.CharField(write_only=True, required=True)
    date_of_birth = serializers.DateField(required=True)
    gender = serializers.CharField(write_only=True, required=True)
    relationship_status = serializers.CharField(write_only=True, required=True)
    phone_number = serializers.CharField(write_only=True, required=True)

    class Meta(BaseUserCreateSerializer.Meta):
        model = User
        fields = (
            "id",
            "email",
            "user_role",
            "password",
            "first_name",
            "middle_name",
            "last_name",
            "date_of_birth",
            "gender",
            "relationship_status",
            "phone_number"
            "address_line_one",
            "address_line_two",
            "address_line_three",
            "province",
            "barangay",
            "city",
            "zip_code",
        )

accounts.views.py

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status

from .serializers import UserCreateSerializer

# Create your views here.
@api_view(["POST"])
def register(request):
    serializer = UserCreateSerializer(data=request.data)

    if serializer.is_valid():
        user = serializer.save(
            address_line_one = request.data["address_line_one"],
            address_line_two = request.data["address_line_two"],
            address_line_three = request.data["address_line_three"],
            province = request.data["province"],
            barangay = request.data["barangay"],
            city = request.data["city"],
            zip_code = request.data["zip_code"],
            first_name = request.data["first_name"],
            middle_name = request.data["middle_name"],
            last_name = request.data["last_name"],
            date_of_birth = request.data["date_of_birth"],
            gender = request.data["gender"],
            relationship_status = request.data["relationship_status"],
            phone_number = request.data["phone_number"]
        )

    return Response(status=201)

accounts.signals.py

from django.db.models.signals import post_save
from django.dispatch import receiver
from django.db import transaction
from django.core.exceptions import ValidationError
from django.contrib.auth import get_user_model
from .models import AddressDetails, UserProfile

User = get_user_model()

@receiver(post_save, sender=User)
def create_address_and_profile(sender, instance, created, **kwargs):
    """
    Signal receiver to create AddressDetails and UserProfile instances
    after a User instance is saved.
    """
    if created:
        print("Created!")
        # Create AddressDetails instance
        address_details = AddressDetails.objects.create(
            address_line_one = kwargs.get("address_line_one", ""),
            address_line_two = kwargs.get("address_line_two", ""),
            address_line_three = kwargs.get("address_line_three", ""),
            province = kwargs.get("province"),
            barangay = kwargs.get("barangay"),
            city = kwargs.get("city"),
            zip_code = kwargs.get("zip_code")
            )

        # Create UserProfile instance
        UserProfile.objects.create(
            user = instance,
            first_name = kwargs.get("first_name"),
            middle_name = kwargs.get("middle_name"),
            last_name = kwargs.get("last_name"),
            date_of_birth = kwargs.get("date_of_birth"),
            gender = kwargs.get("gender"),
            relationship_status = kwargs.get("relationship_status"),
            phone_number = kwargs.get("phone_number"),
            address_details = address_details
            )

accounts.apps.py

from django.apps import AppConfig


class AccountsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'accounts'

    def ready(self):
        from . import signals

config.settings.py

"user_create": "accounts.serializers.UserCreateSerializer", # custom serializer
0

There are 0 best solutions below