DRF 字段类型与验证 #
一、字段类型详解 #
1.1 数值字段 #
python
from rest_framework import serializers
class NumberSerializer(serializers.Serializer):
age = serializers.IntegerField(
min_value=0,
max_value=150,
error_messages={
'min_value': '年龄不能小于0',
'max_value': '年龄不能大于150'
}
)
score = serializers.FloatField(
min_value=0.0,
max_value=100.0
)
price = serializers.DecimalField(
max_digits=10,
decimal_places=2,
min_value=0
)
1.2 字符串字段 #
python
class StringSerializer(serializers.Serializer):
name = serializers.CharField(
max_length=100,
min_length=2,
trim_whitespace=True
)
email = serializers.EmailField()
website = serializers.URLField()
slug = serializers.SlugField()
regex_field = serializers.RegexField(
regex=r'^[a-zA-Z0-9]+$',
error_messages={'invalid': '只能包含字母和数字'}
)
uuid = serializers.UUIDField()
file_path = serializers.FilePathField(
path='/path/to/files',
recursive=True
)
1.3 布尔字段 #
python
class BooleanSerializer(serializers.Serializer):
is_active = serializers.BooleanField()
is_verified = serializers.BooleanField(
default=False,
null=True
)
agree_terms = serializers.BooleanField(
required=True,
error_messages={'required': '必须同意条款'}
)
1.4 日期时间字段 #
python
from datetime import datetime
class DateTimeSerializer(serializers.Serializer):
created_at = serializers.DateTimeField(
format='%Y-%m-%d %H:%M:%S',
input_formats=['%Y-%m-%d', '%Y-%m-%d %H:%M', 'iso-8601']
)
birth_date = serializers.DateField(
format='%Y-%m-%d'
)
meeting_time = serializers.TimeField(
format='%H:%M:%S'
)
duration = serializers.DurationField()
1.5 选择字段 #
python
STATUS_CHOICES = [
('draft', '草稿'),
('published', '已发布'),
('archived', '已归档'),
]
class ChoiceSerializer(serializers.Serializer):
status = serializers.ChoiceField(
choices=STATUS_CHOICES,
default='draft'
)
multiple_choice = serializers.MultipleChoiceField(
choices=STATUS_CHOICES
)
1.6 文件字段 #
python
class FileSerializer(serializers.Serializer):
file = serializers.FileField(
max_length=100,
allow_empty_file=False,
use_url=True
)
image = serializers.ImageField(
max_length=100,
allow_empty_file=False
)
1.7 复杂字段 #
python
class ComplexSerializer(serializers.Serializer):
tags = serializers.ListField(
child=serializers.CharField(max_length=50),
allow_empty=False,
min_length=1,
max_length=10
)
metadata = serializers.DictField(
child=serializers.CharField()
)
json_field = serializers.JSONField()
points = serializers.ListField(
child=serializers.ListField(
child=serializers.FloatField(),
min_length=2,
max_length=2
)
)
二、字段参数详解 #
2.1 通用参数 #
python
class CommonParamsSerializer(serializers.Serializer):
field = serializers.CharField(
required=True,
default='',
allow_null=False,
allow_blank=False,
read_only=False,
write_only=False,
label='字段标签',
help_text='字段帮助文本',
error_messages={
'required': '此字段必填',
'blank': '不能为空字符串',
'null': '不能为null'
}
)
2.2 参数说明表 #
| 参数 | 类型 | 说明 |
|---|---|---|
| required | bool | 是否必需 |
| default | any | 默认值 |
| allow_null | bool | 是否允许null |
| allow_blank | bool | 是否允许空字符串 |
| read_only | bool | 只读字段 |
| write_only | bool | 只写字段 |
| label | str | 字段标签 |
| help_text | str | 帮助文本 |
| validators | list | 验证器列表 |
| error_messages | dict | 错误消息 |
2.3 数值字段参数 #
python
class NumberParamsSerializer(serializers.Serializer):
value = serializers.IntegerField(
min_value=0,
max_value=100
)
decimal_value = serializers.DecimalField(
max_digits=10,
decimal_places=2,
min_value=0,
max_value=99999999.99,
rounding='ROUND_HALF_UP'
)
2.4 字符串字段参数 #
python
class StringParamsSerializer(serializers.Serializer):
text = serializers.CharField(
max_length=100,
min_length=1,
trim_whitespace=True,
allow_blank=False
)
三、内置验证器 #
3.1 UniqueValidator #
python
from rest_framework.validators import UniqueValidator
class UserSerializer(serializers.Serializer):
username = serializers.CharField(
validators=[
UniqueValidator(queryset=User.objects.all())
]
)
email = serializers.EmailField(
validators=[
UniqueValidator(
queryset=User.objects.all(),
message='该邮箱已被注册'
)
]
)
3.2 UniqueTogetherValidator #
python
from rest_framework.validators import UniqueTogetherValidator
class ArticleSerializer(serializers.Serializer):
class Meta:
validators = [
UniqueTogetherValidator(
queryset=Article.objects.all(),
fields=['title', 'author'],
message='该作者已存在相同标题的文章'
)
]
3.3 BaseUniqueValidator #
python
from rest_framework.validators import BaseUniqueValidator
class CustomUniqueValidator(BaseUniqueValidator):
def filter_queryset(self, value, queryset):
return queryset.filter(field=value)
3.4 ProhibitSurrogateCharactersValidator #
python
from rest_framework.validators import ProhibitSurrogateCharactersValidator
class TextSerializer(serializers.Serializer):
content = serializers.CharField(
validators=[ProhibitSurrogateCharactersValidator()]
)
四、自定义验证 #
4.1 字段级验证 #
python
class ArticleSerializer(serializers.Serializer):
title = serializers.CharField(max_length=200)
def validate_title(self, value):
if len(value) < 5:
raise serializers.ValidationError('标题至少需要5个字符')
forbidden_words = ['广告', '推销', '垃圾']
for word in forbidden_words:
if word in value:
raise serializers.ValidationError(f'标题不能包含"{word}"')
return value
4.2 对象级验证 #
python
class EventSerializer(serializers.Serializer):
title = serializers.CharField()
start_date = serializers.DateField()
end_date = serializers.DateField()
max_participants = serializers.IntegerField()
current_participants = serializers.IntegerField()
def validate(self, data):
if data['start_date'] > data['end_date']:
raise serializers.ValidationError({
'end_date': '结束日期不能早于开始日期'
})
if data['current_participants'] > data['max_participants']:
raise serializers.ValidationError('当前参与人数不能超过最大人数')
return data
4.3 验证器类 #
python
class ForbiddenWordsValidator:
def __init__(self, forbidden_words):
self.forbidden_words = forbidden_words
def __call__(self, value):
for word in self.forbidden_words:
if word in value:
raise serializers.ValidationError(f'不能包含"{word}"')
class ArticleSerializer(serializers.Serializer):
title = serializers.CharField(
validators=[ForbiddenWordsValidator(['广告', '垃圾'])]
)
4.4 复杂验证器 #
python
import re
class PasswordValidator:
def __init__(self, min_length=8):
self.min_length = min_length
def __call__(self, value):
if len(value) < self.min_length:
raise serializers.ValidationError(
f'密码至少需要{self.min_length}个字符'
)
if not re.search(r'[A-Z]', value):
raise serializers.ValidationError('密码必须包含大写字母')
if not re.search(r'[a-z]', value):
raise serializers.ValidationError('密码必须包含小写字母')
if not re.search(r'[0-9]', value):
raise serializers.ValidationError('密码必须包含数字')
if not re.search(r'[!@#$%^&*]', value):
raise serializers.ValidationError('密码必须包含特殊字符')
class UserSerializer(serializers.Serializer):
password = serializers.CharField(
validators=[PasswordValidator(min_length=8)]
)
五、条件验证 #
5.1 基于其他字段的验证 #
python
class ArticleSerializer(serializers.Serializer):
is_published = serializers.BooleanField(default=False)
published_at = serializers.DateTimeField(required=False)
def validate(self, data):
if data.get('is_published') and not data.get('published_at'):
raise serializers.ValidationError({
'published_at': '发布文章必须设置发布时间'
})
return data
5.2 基于请求的验证 #
python
class ArticleSerializer(serializers.Serializer):
title = serializers.CharField()
def validate_title(self, value):
request = self.context.get('request')
if request and not request.user.is_staff:
if len(value) > 50:
raise serializers.ValidationError('普通用户标题不能超过50字符')
return value
5.3 基于实例的验证 #
python
class ArticleSerializer(serializers.Serializer):
status = serializers.CharField()
def validate_status(self, value):
instance = self.instance
if instance and instance.status == 'published':
if value != 'published':
raise serializers.ValidationError('已发布的文章不能修改状态')
return value
六、错误消息处理 #
6.1 自定义错误消息 #
python
class ArticleSerializer(serializers.Serializer):
title = serializers.CharField(
max_length=200,
error_messages={
'required': '标题不能为空',
'blank': '标题不能为空白',
'max_length': '标题不能超过{max_length}个字符',
'min_length': '标题至少需要{min_length}个字符'
}
)
6.2 动态错误消息 #
python
class ArticleSerializer(serializers.Serializer):
title = serializers.CharField(max_length=200)
def validate_title(self, value):
if 'test' in value.lower():
raise serializers.ValidationError(
detail={'title': '标题不能包含"test"'},
code='invalid_title'
)
return value
6.3 格式化错误响应 #
python
from rest_framework.exceptions import ValidationError
def format_errors(errors):
formatted = []
for field, messages in errors.items():
if isinstance(messages, list):
for message in messages:
formatted.append({
'field': field,
'message': message
})
elif isinstance(messages, dict):
for sub_field, sub_messages in messages.items():
for message in sub_messages:
formatted.append({
'field': f'{field}.{sub_field}',
'message': message
})
return formatted
class ArticleSerializer(serializers.Serializer):
title = serializers.CharField()
def validate(self, data):
errors = {}
if not data.get('title'):
errors['title'] = ['标题不能为空']
if errors:
raise ValidationError(format_errors(errors))
return data
七、字段组合验证 #
7.1 至少一个字段必填 #
python
class SearchSerializer(serializers.Serializer):
keyword = serializers.CharField(required=False)
category = serializers.CharField(required=False)
author = serializers.CharField(required=False)
def validate(self, data):
if not any([data.get('keyword'), data.get('category'), data.get('author')]):
raise serializers.ValidationError('至少需要提供一个搜索条件')
return data
7.2 互斥字段 #
python
class PaymentSerializer(serializers.Serializer):
credit_card = serializers.CharField(required=False)
bank_account = serializers.CharField(required=False)
def validate(self, data):
credit_card = data.get('credit_card')
bank_account = data.get('bank_account')
if credit_card and bank_account:
raise serializers.ValidationError('只能选择一种支付方式')
if not credit_card and not bank_account:
raise serializers.ValidationError('必须选择一种支付方式')
return data
7.3 条件必填 #
python
class OrderSerializer(serializers.Serializer):
delivery_type = serializers.ChoiceField(choices=['pickup', 'delivery'])
address = serializers.CharField(required=False)
def validate(self, data):
if data['delivery_type'] == 'delivery' and not data.get('address'):
raise serializers.ValidationError({
'address': '送货方式必须填写地址'
})
return data
八、高级验证技巧 #
8.1 异步验证 #
python
import asyncio
async def check_email_exists(email):
await asyncio.sleep(0.1)
return User.objects.filter(email=email).exists()
class UserSerializer(serializers.Serializer):
email = serializers.EmailField()
def validate_email(self, value):
loop = asyncio.new_event_loop()
exists = loop.run_until_complete(check_email_exists(value))
if exists:
raise serializers.ValidationError('该邮箱已被使用')
return value
8.2 外部API验证 #
python
import requests
class AddressSerializer(serializers.Serializer):
zip_code = serializers.CharField()
def validate_zip_code(self, value):
response = requests.get(f'https://api.zipcode.com/{value}')
if response.status_code != 200:
raise serializers.ValidationError('无效的邮政编码')
return value
8.3 数据库查询验证 #
python
class ArticleSerializer(serializers.Serializer):
category_id = serializers.IntegerField()
def validate_category_id(self, value):
if not Category.objects.filter(id=value).exists():
raise serializers.ValidationError('分类不存在')
return value
九、验证最佳实践 #
9.1 验证器复用 #
python
def create_forbidden_words_validator(words):
def validator(value):
for word in words:
if word in value:
raise serializers.ValidationError(f'不能包含"{word}"')
return value
return validator
class ArticleSerializer(serializers.Serializer):
title = serializers.CharField(
validators=[create_forbidden_words_validator(['广告', '垃圾'])]
)
content = serializers.CharField(
validators=[create_forbidden_words_validator(['违禁词'])]
)
9.2 验证器组合 #
python
from rest_framework.validators import UniqueValidator
def validate_length(min_len, max_len):
def validator(value):
if len(value) < min_len or len(value) > max_len:
raise serializers.ValidationError(
f'长度必须在{min_len}到{max_len}之间'
)
return value
return validator
class UserSerializer(serializers.Serializer):
username = serializers.CharField(
validators=[
UniqueValidator(queryset=User.objects.all()),
validate_length(3, 20),
]
)
十、总结 #
本章学习了DRF的字段类型和验证:
- 字段类型:数值、字符串、日期、文件等各种类型
- 字段参数:控制字段行为的各种参数
- 内置验证器:UniqueValidator等内置验证器
- 自定义验证:字段级、对象级和验证器类
- 条件验证:基于其他字段或请求的验证
- 错误处理:自定义错误消息和格式化
掌握验证机制是构建健壮API的关键,让我们继续学习嵌套序列化器!
最后更新:2026-03-28