BaseSerializer 中的自定义验证器
Custom validators in BaseSerializer
我想在 BaseSerializer 中使用自定义验证器。
第一次尝试:
自定义验证器:
class UserIdExists:
def __init__(self):
self.MESSAGE_INCORRECT_USER = 'No user with this id.'
def __call__(self, user_id):
exists = User.objects.filter(id=user_id).exists()
if not exists:
message = self.MESSAGE_INCORRECT_USER
raise serializers.ValidationError(message, code='invalid')
和BaseSerializer:
class QuestionBaseSerializer(serializers.BaseSerializer):
def to_internal_value(self, data):
user_id = data.get('user_id')
user_validator = UserIdExists()
user_validator(user_id=user_id)
如果出现验证错误,我收到一条消息:
'["No user with this id."]'
但是我想收到这样一条信息:
'{"user_id": "No user with this id."}'
第二次尝试:
自定义验证器:
class UserIdExists:
def __init__(self):
self.MESSAGE_INCORRECT_USER = 'No user with this id.'
def __call__(self, user_id, field=None):
exists = User.objects.filter(id=user_id).exists()
if not exists:
if field:
message = {field: self.MESSAGE_INCORRECT_USER}
raise serializers.ValidationError(message, code='invalid')
else:
if not field:
message = self.MESSAGE_INCORRECT_USER
raise serializers.ValidationError(message, code='invalid')
和BaseSerializer:
class QuestionBaseSerializer(serializers.BaseSerializer):
def to_internal_value(self, data):
user_id = data.get('user_id')
user_validator = UserIdExists()
user_validator(user_id=user_id, field='user_id')
我收到预期的消息:
'{"user_id": "No user with this id."}'
我做的对吗?可以做得更正确吗?
与其使用 to_internal_value
,不如使用专门用于验证的功能,这样 extend/maintain 会更容易,因为您只需要关注验证逻辑,而不是如何产生内部价值。验证字段的约定是通过:
- 序列化器字段属性validators(字段的验证器列表运行)
- 序列化器方法validate_field_name()
- 序列化器方法
validate()
这里为了简化说明,我们将验证只允许奇数user_id
。
class UserIdExists:
def __init__(self):
self.MESSAGE_INCORRECT_USER = 'No user with this id.'
def __call__(self, user_id):
if user_id % 2 == 0:
raise serializers.ValidationError("No user with this id.")
return user_id
class QuestionBaseSerializer(serializers.Serializer):
user_id = serializers.IntegerField(
validators=[UserIdExists()]
)
class MathQuestionSerializer(QuestionBaseSerializer):
description = serializers.CharField()
>>> from my_app.serializers import MathQuestionSerializer
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 2, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
Traceback (most recent call last):
raise ValidationError(self.errors)
rest_framework.exceptions.ValidationError: {'user_id': [ErrorDetail(string='No user with this id.', code='invalid')]}
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 3, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
True
如您所见,从 QuestionBaseSerializer
继承是有效的,因为它自动正确验证了 user_id
字段,确保它不是偶数。
您可以通过实施 validate_field_name()
:
选择第二种样式来进一步自定义验证
class QuestionBaseSerializer(serializers.Serializer):
user_id = serializers.IntegerField()
def validate_user_id(self, user_id: int) -> int:
validator = UserIdExists()
return validator(user_id)
class MathQuestionSerializer(QuestionBaseSerializer):
description = serializers.CharField()
def validate_user_id(self, user_id: int) -> int:
# Let's call the usual validation of even-odd numbers
user_id_validated = super().validate_user_id(user_id)
# Now, let's add additional validation of small numbers
if user_id_validated < 10:
raise serializers.ValidationError("No user with this id.")
return user_id_validated
>>> serializer = MathQuestionSerializer(data={'user_id': 2, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
Traceback (most recent call last):
raise ValidationError(self.errors)
rest_framework.exceptions.ValidationError: {'user_id': [ErrorDetail(string='No user with this id.', code='invalid')]}
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 3, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
Traceback (most recent call last):
raise ValidationError(self.errors)
rest_framework.exceptions.ValidationError: {'user_id': [ErrorDetail(string='No user with this id.', code='invalid')]}
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 14, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
Traceback (most recent call last):
raise ValidationError(self.errors)
rest_framework.exceptions.ValidationError: {'user_id': [ErrorDetail(string='No user with this id.', code='invalid')]}
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 15, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
True
如您所见,验证正确地检查了它是否为奇数(基本验证)并且是否大于 10(子类验证)。
我想在 BaseSerializer 中使用自定义验证器。
第一次尝试:
自定义验证器:
class UserIdExists:
def __init__(self):
self.MESSAGE_INCORRECT_USER = 'No user with this id.'
def __call__(self, user_id):
exists = User.objects.filter(id=user_id).exists()
if not exists:
message = self.MESSAGE_INCORRECT_USER
raise serializers.ValidationError(message, code='invalid')
和BaseSerializer:
class QuestionBaseSerializer(serializers.BaseSerializer):
def to_internal_value(self, data):
user_id = data.get('user_id')
user_validator = UserIdExists()
user_validator(user_id=user_id)
如果出现验证错误,我收到一条消息:
'["No user with this id."]'
但是我想收到这样一条信息:
'{"user_id": "No user with this id."}'
第二次尝试: 自定义验证器:
class UserIdExists:
def __init__(self):
self.MESSAGE_INCORRECT_USER = 'No user with this id.'
def __call__(self, user_id, field=None):
exists = User.objects.filter(id=user_id).exists()
if not exists:
if field:
message = {field: self.MESSAGE_INCORRECT_USER}
raise serializers.ValidationError(message, code='invalid')
else:
if not field:
message = self.MESSAGE_INCORRECT_USER
raise serializers.ValidationError(message, code='invalid')
和BaseSerializer:
class QuestionBaseSerializer(serializers.BaseSerializer):
def to_internal_value(self, data):
user_id = data.get('user_id')
user_validator = UserIdExists()
user_validator(user_id=user_id, field='user_id')
我收到预期的消息:
'{"user_id": "No user with this id."}'
我做的对吗?可以做得更正确吗?
与其使用 to_internal_value
,不如使用专门用于验证的功能,这样 extend/maintain 会更容易,因为您只需要关注验证逻辑,而不是如何产生内部价值。验证字段的约定是通过:
- 序列化器字段属性validators(字段的验证器列表运行)
- 序列化器方法validate_field_name()
- 序列化器方法
validate()
这里为了简化说明,我们将验证只允许奇数user_id
。
class UserIdExists:
def __init__(self):
self.MESSAGE_INCORRECT_USER = 'No user with this id.'
def __call__(self, user_id):
if user_id % 2 == 0:
raise serializers.ValidationError("No user with this id.")
return user_id
class QuestionBaseSerializer(serializers.Serializer):
user_id = serializers.IntegerField(
validators=[UserIdExists()]
)
class MathQuestionSerializer(QuestionBaseSerializer):
description = serializers.CharField()
>>> from my_app.serializers import MathQuestionSerializer
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 2, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
Traceback (most recent call last):
raise ValidationError(self.errors)
rest_framework.exceptions.ValidationError: {'user_id': [ErrorDetail(string='No user with this id.', code='invalid')]}
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 3, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
True
如您所见,从 QuestionBaseSerializer
继承是有效的,因为它自动正确验证了 user_id
字段,确保它不是偶数。
您可以通过实施 validate_field_name()
:
class QuestionBaseSerializer(serializers.Serializer):
user_id = serializers.IntegerField()
def validate_user_id(self, user_id: int) -> int:
validator = UserIdExists()
return validator(user_id)
class MathQuestionSerializer(QuestionBaseSerializer):
description = serializers.CharField()
def validate_user_id(self, user_id: int) -> int:
# Let's call the usual validation of even-odd numbers
user_id_validated = super().validate_user_id(user_id)
# Now, let's add additional validation of small numbers
if user_id_validated < 10:
raise serializers.ValidationError("No user with this id.")
return user_id_validated
>>> serializer = MathQuestionSerializer(data={'user_id': 2, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
Traceback (most recent call last):
raise ValidationError(self.errors)
rest_framework.exceptions.ValidationError: {'user_id': [ErrorDetail(string='No user with this id.', code='invalid')]}
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 3, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
Traceback (most recent call last):
raise ValidationError(self.errors)
rest_framework.exceptions.ValidationError: {'user_id': [ErrorDetail(string='No user with this id.', code='invalid')]}
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 14, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
Traceback (most recent call last):
raise ValidationError(self.errors)
rest_framework.exceptions.ValidationError: {'user_id': [ErrorDetail(string='No user with this id.', code='invalid')]}
>>>
>>> serializer = MathQuestionSerializer(data={'user_id': 15, 'description': "What is an imaginary number?"})
>>> serializer.is_valid(raise_exception=True)
True
如您所见,验证正确地检查了它是否为奇数(基本验证)并且是否大于 10(子类验证)。