在 DRF ModelViewSet 上创建用户时的自定义错误消息

Custom error message on Create User on DRF ModelViewSet

我正在使用 Django Rest Framework 编写 Django Rest API,其中我有一个基本自定义用户 User 和两种继承自 User 的用户,DoctorPacient.

每个人都有不同的端点,因此要创建一个 Doctor,我们向 /api/v1/doctor 发出一个 POST 请求并创建一个 Paciente,一个 POSTapi/v1/pacient/.

如果我尝试创建一个用户(使用这些端点)但使用已经注册的电子邮件(我已将 Django 更改为使用电子邮件),我会得到以下信息。

{
    "email": [
        "user with this email already exists."
    ]
}

我想要实现的是更改此错误消息,但不仅要更改,我还需要验证电子邮件是来自 Pacient 还是 Doctor,无论是哪个我正在使用的端点,因此我可以针对每种情况显示不同的消息。

PacientViewSet(DoctorViewSet基本一样):

class PacientViewSet(viewsets.ModelViewSet):
    serializer_class = PacientSerializer
    queryset = Pacient.objects.all()
    permission_classes = (AllowAny, )

序列化器:

class PacientSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        user = Paciente.objects.create_paciente_user(
            nome_completo=self.validated_data['full_name'],
            password=self.validated_data['password'],
            email=self.validated_data['email'],
        )
        return user

    class Meta:
        model = Pacient
        fields = ('id', 'email', 'password', 'full_name', ...)
        extra_kwargs = {
            'password': {
                'write_only': True,
            }
        }

如果还有什么需要分享的,请告诉我。

编辑:

class CustomUser(AbstractBaseUser, PermissionsMixin):
    full_name = models.CharField(max_length=100, null=True, blank=True)
    email = models.EmailField(_('email address'), unique=True)

    SEX_CHOICES = [
        (ConstantesSexo.MALE, 'Male'),
        (ConstantesSexo.FEMALE, 'Female'),
    ]
    sex = models.CharField(max_length=1, choices=SEX_CHOICES, null=True, blank=True)

    is_staff = models.BooleanField(_('staff status'), default=False)
    is_active = models.BooleanField(_('active'), default=True)

    created = models.DateTimeField(_('date joined'), default=timezone.now)
    modified = models.DateTimeField(auto_now=True)

    username_validator = UnicodeUsernameValidator()
    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        blank=True,
        null=True,
        validators=[username_validator],
        error_messages={
            'unique': _("Username already exists."),
        },
    )
    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    USER_CHOICES = [
        (UserType.DOCTOR, 'Doctor'),
        (UserType.PACIENT, 'Pacient'),
    ]
    user_type = models.CharField(max_length=3, choices=USER_CHOICES)


class Pacient(CustomUser):
    nome_social = models.CharField(blank=True, null=True, max_length=50)


class Doctor(CustomUser):
    doctor_id = models.CharField(max_length=50)
    bio = models.TextField(blank=True, null=True)
    verified = models.DateTimeField(null=True, blank=True)

据我所知,有两种方法可以实现此目的

第一个会在 PacientSerializer create 方法中引发错误

class PacientSerializer(serializers.ModelSerializer):
    def create(self, validated_data):
        if Paciente.objects.filter(email=validated_data["email"]).exists():
            raise serializers.ValidationError("Your Custom Error Message")

        user = Paciente.objects.create_paciente_user(
            nome_completo=validated_data["full_name"],
            password=validated_data["password"],
            email=validated_data["email"],
        )
        return user

另一种方法是覆盖

def finalize_response(self, request, response, *args, **kwargs):
    # change response.data dictionary accordingly
    ...
    return response 

最后我通过覆盖 ModelViewSet 中的 create 方法(而不是在序列化程序中)解决了这个问题:

class DoctorViewSet(viewsets.ModelViewSet):

    def create(self, request, *args, **kwargs):
        email = request.data.get('email')
        user = CustomUser.objects.filter(email=email).first()
        if email and user:
            if user.is_doctor():
                error_msg = 'doctor error msg'
            elif user.is_pacient():
                error_msg = 'pacient error msg'
            return Response({'error': error_msg}, status=status.HTTP_400_BAD_REQUEST)

        return super().create(request, *args, **kwargs)

我知道这是一个老问题,我一直在尝试找到一个答案来更改电子邮件地址的唯一错误消息,但答案对于这么小的更改来说工作量太大,所以我调试了验证流程框架并提出了这个解决方案。我把它留在这里给寻找解决方案的人

我正在使用 dj_rest_auth 所以我有一个自定义注册序列化程序,它从 dj_rest_auth.registration.serializers.RegisterSerializer 扩展而来 所以我只需要在我的自定义序列化程序中覆盖以下方法,它就会按预期工作,它会覆盖唯一电子邮件的错误消息

def validate_email(self, email):
    try:
        email = super(CustomRegisterSerializer, self).validate_email(email);
    except Exception as e:
            raise serializers.ValidationError(
                _("Please try a different email."))
    return email

dj_rest_auth.registration.serializers.RegisterSerializer 有这个方法,我们正在重写以仅更改错误消息,我们也可以在其中添加我们自己的代码,但我只需要更改错误。

我发现的另一种方法是向我拥有的这个自定义序列化器添加一个 class 级别字段,即

    email = serializers.EmailField(validators=[UniqueValidator(queryset=User.objects.all(),message="Please try a different email.")])

但是这个 运行 是一个额外的查询 User.objects.all() 所以想象一下有很多记录然后这可能会减慢响应速度