首页 > 解决方案 > 为什么要使用 Django REST 框架 ModelSerializer。创建和 ModelSerializer.validate 不能正常工作?

问题描述

我正在开发一个通过 Django rest 框架进行用户注册的身份验证系统。

序列化程序.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

视图.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)

模型.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)

问题是当我在邮递员中测试 API 时,它会给出以下输出:

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

但是,在 POSTMAN 中测试 POST 方法时,我输入了所有数据。

截屏:

在此处输入图像描述

在进行故障排除时,我发现在“create”函数内的 serializers.py 中我什么也没返回。所以,我补充说

return NewEmployeeProfile.objects.create(**validated_data)

现在,当我测试我的 API 时,POST 提交进入无限循环,在 POSTMAN 的“正文”中没有输出。

我想,我没有正确覆盖“创建”和“验证”方法。

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'),
    ]

项目.url

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

新错误:

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

标签: djangodjango-rest-frameworkdjango-serializer

解决方案


试试这个,

from django.contrib.auth.hashers import make_password

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

def validate(self, attrs):
    if attrs.get("password") != attrs.pop("password2"):
        raise serializers.ValidationError({"password": f"password must match.."})
    return super().validate(attrs)

此外,表单数据中名字和姓氏的键必须是first_namelast_name

更新#1:

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

def validate(self, attrs):
    if attrs.get("password") != attrs.pop("password2"):
        raise serializers.ValidationError({"password": f"password must match.."})
    return super().validate(attrs)

# Also update the view
class UserRegisterView(ListCreateAPIView):
    create_queryset = NewEmployeeProfile.objects.all()
    serializer_class = RegistrationSerializers
    permission_classes = [AllowAny]

    # A custom create method is not required

推荐阅读