Djoser 覆盖 UserCreateSerializer 不创建模型实例

问题描述 投票:0回答:1

我对 Djoser 提供的 UserCreateSerializer 进行了修改。在 Perform_create 方法中,我尝试创建 UserProfile 和 AddressDetails 模型的实例。虽然服务器似乎运行正常,但数据似乎并未按预期持续存在。我正在寻求有关识别和纠正此过程中任何错误的指导。您建议如何解决这个问题?

accounts.serializers.py

from djoser.serializers import UserCreateSerializer
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(UserCreateSerializer):
    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()
    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(UserCreateSerializer.Meta):
        model = User
        fields = (
            "id",
            "email",
            "user_role",
            "password",
            "address_line_one",
            "address_line_two",
            "address_line_three",
            "province",
            "barangay",
            "city",
            "zip_code",
            "first_name",
            "middle_name",
            "last_name",
            "date_of_birth",
            "gender",
            "relationship_status",
            "phone_number"
        )
    
    @transaction.atomic
    def perform_create(self, validated_data):
        address_line_one = validated_data.pop("address_line_one")
        address_line_two = validated_data.pop("address_line_two")
        address_line_three = validated_data.pop("address_line_three")
        province = validated_data.pop("province")
        barangay = validated_data.pop("barangay")
        city = validated_data.pop("city")
        zip_code = validated_data.pop("zip_code")
        first_name = validated_data.pop("first_name")
        middle_name = validated_data.pop("middle_name")
        last_name = validated_data.pop("last_name")
        date_of_birth = validated_data.pop("date_of_birth")
        gender = validated_data.pop("gender")
        relationship_status = validated_data.pop("relationship_status")
        phone_number = validated_data.pop("phone_number")

        with transaction.atomic():
            user = User.objects.create_user(**validated_data)
            
            address_details = AddressDetails.objects.create(
                address_line_one = address_line_one,
                address_line_two = address_line_two,
                address_line_three = address_line_three,
                province = province,
                barangay = barangay,
                city = city,
                zip_code = zip_code
            )

            UserProfile.objects.create(
                user = user,
                first_name = first_name,
                middle_name = middle_name,
                last_name = last_name,
                date_of_birth = date_of_birth,
                gender = gender,
                relationship_status = relationship_status,
                phone_number = phone_number,
                address_details = address_details
            )

        return user
    
    def validate_password(self, value):
        user = self.user if self.user else self.Meta.model()

        try:
            validate_password(value, user)
        except serializers.ValidationError as e:
            serializer_error = serializers.as_serializer_error(e)
            raise serializers.ValidationError(
                {"password": serializer_error[api_setting.NON_FIELDS_ERRORS_KEY]}
            )

        return value

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 = PhoneNumberField()
    address_details = models.OneToOneField(AddressDetails, on_delete=models.CASCADE)
django serialization django-rest-framework djoser
1个回答
0
投票

我最近遇到了同样的问题,我假设您已经在 settings.py:

中添加了这一行
DJOSER = {
    ...
    "SERIALIZERS": {
    "user_create": "accounts.serializers.UserCreateSerializer",
    ...
    }
}

如果您的设置中没有此功能,请添加它,问题很可能仍然存在。 解决方案是将以下行添加到之前提到的“SERIALIZERS”字典中

"user_create_password_retype": "accounts.serializers.UserCreateSerializer"

这对我有用,我使用 djoser 版本 2.2.2

在检查 djoser 库中的 views.py 中的 UserViewSet 时发现了此方法起作用的原因:

def get_serializer_class(self):
    if self.action == "create":
        if settings.USER_CREATE_PASSWORD_RETYPE:
            return settings.SERIALIZERS.user_create_password_retype
        return settings.SERIALIZERS.user_create

USER_CREATE_PASSWORD_RETYPE 序列化程序会覆盖 settings.py 中指定的 user_create 序列化程序,这就是它返回到默认 djoser 的原因 UserCreateSerializer

© www.soinside.com 2019 - 2024. All rights reserved.