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