测试开发进阶(二十六)

测试开发进阶(二十六)

欢迎关注我的公众号「测试游记」

序列化器

  1. 序列化器中定义的类属性字段,往往与模型类字段一一对应
  2. label选项相当于verbose_name;
  3. 定义的序列化器字段,默认既可以进行序列化输出,也可以进行反序列化输入

    通用参数

  • read_only = True 指定该字段只能进行序列化输出
  • write_only = True 指定该字段只进行反序列化输入,但不进行序列化输出
  • required该字段在反序列化时必须传入,默认为True
  • default反序列化时使用的默认值
  • allow_null该字段是否允许传入None,默认False
  • validators该字段使用的校验器
  • error_messages包含错误key与错误信息的字典(下面有例子)
  • label用于HTML展示API页面时,显示的字段名称

选项参数

  • max_length最大长度
  • min_length最小长度
  • allow_blank是否允许为空
  • trim_whitespace是否截断空白字符
  • max_value最小值
  • min_value最大值

校验

  1. 调用序列化器对象的is_valid方法,才开始校验前端参数
  2. 如果校验成功,则返回True,校验失败返回False
  3. raise_exception=True,校验失败后会抛出异常
  4. 当调用is_valid方法之后,才可以调用errors属性「校验的错误提示(字典)」
1
2
3
4
try:
serializer.is_valid(raise_exception=True)
except Exception as e:
return JsonResponse(serializer.errors)

在序列化器中增加validators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from rest_framework.validators import UniqueValidator
from projects.models import Projects

name = serializers.CharField(
label='项目名称',
max_length=200,
help_text='项目名称',
validators=[
UniqueValidator(
queryset=Projects.objects.all(),
message='项目名不能重复'
)
]
)

编写一份创建项目的json
创建项目json

发送一个创建请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ http POST :8000/project/ < projects.json 
zhongxindeMacBook-Pro:apitest zhongxin$ http POST :8000/project/ < projects.json
HTTP/1.1 200 OK
Content-Length: 56
Content-Type: application/json
Date: Wed, 16 Oct 2019 12:34:16 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Origin
X-Frame-Options: SAMEORIGIN

{
"name": [
"项目名不能重复"
]
}

请求结果

自定义校验

自己编写一个校验器

需求:项目名称中必须包含项目关键字

1
2
3
4
5
6
7
8
def is_unique_project_name(name):
"""
项目名称中需要包含「项目」关键字
:param name:前端传递给我的项目名称
:return:
"""
if '项目' not in name:
raise serializers.ValidationError(detail='项目名称中必须包含「项目」')
1
2
3
4
5
6
7
8
9
10
11
name = serializers.CharField(
label='项目名称',
max_length=200,
help_text='项目名称',
validators=[
UniqueValidator(
queryset=Projects.objects.all(),
message='项目名不能重复'
), is_unique_project_name
]
)

请求结果

单字段校验

在序列化器内部增加def validate_name(self, value):

1
2
3
4
# 单字段的校验
def validate_name(self, value):
if not value.endswith('项目'):
raise serializers.ValidationError('项目名称必须以「项目」结尾')
1
2
3
4
5
6
7
8
9
# projects.json
{
"name": "测试游记1",
"leader": "zhongxin",
"tester": "zhongxin",
"programer": "zhong3",
"publish_app": "公众号",
"desc": "无"
}

校验

修改projects.json

1
2
3
4
5
6
7
8
9
# projects.json
{
"name": "测试游记项目1",
"leader": "zhongxin",
"tester": "zhongxin",
"programer": "zhong3",
"publish_app": "公众号",
"desc": "无"
}

测试2

多字段校验

1
2
def validate(self, attrs):
pass

多字段

这里nameNone是因为上面单字段校验没有返回value

修改后再次测试

1
2
3
4
5
def validate_name(self, value):
if not value.endswith('项目'):
raise serializers.ValidationError('项目名称必须以「项目」结尾')
else:
return value

测试2

1
2
3
4
5
6
7
8
9
10
11
def validate(self, attrs):
"""
多字段联合校验
需求:tester和leader中有「icon」
:param attrs:
:return:
"""
if 'icon' not in attrs['tester'] and 'icon' not in attrs['leader']:
raise serializers.ValidationError('「icon」必须是项目负责人或者在项目的测试人员')
else:
return attrs

测试

修改projects.json

1
2
3
4
5
6
7
8
9
# projects.json
{
"name": "测试游记项目",
"leader": "icon",
"tester": "zhongxin",
"programer": "zhong3",
"publish_app": "公众号",
"desc": "无"
}

正确测试

校验器的顺序

  1. 字段定义时的限制,包含validators列表条目从左到右进行校验
  2. 单字段「validate_字段名」的校验
  3. 多字段联合校验「validate方法」

优化视图内代码

  • 如果在创建序列化器对象时候,只给data传参,那么调用save()方法实际调用的就是序列化器对象的create()方法

  • 在创建序列化器对象时,同时给instance和data传参,那么调用save()方法实际调用的就是序列化器对象的update()方法

序列化器类ProjectSerializer中增加createupdate

1
2
3
4
5
6
7
8
9
10
11
12
def create(self, validated_data):
return Projects.objects.create(**validated_data)

def update(self, instance, validated_data):
instance.name = validated_data['name']
instance.leader = validated_data['leader']
instance.tester = validated_data['tester']
instance.programer = validated_data['programer']
instance.publish_app = validated_data['publish_app']
instance.desc = validated_data['desc']
instance.save()
return instance

修改post请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def post(self, request):
"""
新建项目
"""
json_data = request.body.decode('utf8')
python_data = json.loads(json_data, encoding='utf8')
serializer = ProjectSerializer(data=python_data)
try:
serializer.is_valid(raise_exception=True)
except Exception as e:
return JsonResponse(serializer.errors)
project = Projects.objects.create(**serializer.validated_data)
serializer = ProjectSerializer(instance=project)
return JsonResponse(serializer.data, status=201)

改为

1
2
3
4
5
6
7
8
9
10
11
12
13
def post(self, request):
"""
新建项目
"""
json_data = request.body.decode('utf8')
python_data = json.loads(json_data, encoding='utf8')
serializer = ProjectSerializer(data=python_data)
try:
serializer.is_valid(raise_exception=True)
except Exception as e:
return JsonResponse(serializer.errors)
serializer.save()
return JsonResponse(serializer.data, status=201)

修改put请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def put(self, request, pk):
project = self.get_object(pk)
json_data = request.body.decode('utf8')
python_data = json.loads(json_data, encoding='utf8')
serializer = ProjectSerializer(data=python_data)
try:
serializer.is_valid(raise_exception=True)
except Exception as e:
return JsonResponse(serializer.errors)
project.name = serializer.validated_data['name']
project.leader = serializer.validated_data['leader']
project.tester = serializer.validated_data['tester']
project.programer = serializer.validated_data['programer']
project.publish_app = serializer.validated_data['publish_app']
project.desc = serializer.validated_data['desc']
project.save()

serializer = ProjectSerializer(instance=project)
return JsonResponse(serializer.data, status=201)

改为

1
2
3
4
5
6
7
8
9
10
11
def put(self, request, pk):
project = self.get_object(pk)
json_data = request.body.decode('utf8')
python_data = json.loads(json_data, encoding='utf8')
serializer = ProjectSerializer(instance=project, data=python_data)
try:
serializer.is_valid(raise_exception=True)
except Exception as e:
return JsonResponse(serializer.errors)
serializer.save()
return JsonResponse(serializer.data, status=201)

优化序列化器

1
2
3
4
5
6
7
8
from rest_framework import serializers

class ProjectModelSerializer(serializers.ModelSerializer):
class Meta:
# 指定参考哪一个模型类来创建
model = Projects
# 指定为模型类的哪些字段,来生成序列化器
fields = '__all__'
1
2
3
4
5
6
7
8
9
10
11
12
In[2]:from projects import serializer
In[3]:serializer.ProjectModelSerializer()
Out[3]:
ProjectModelSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(help_text='项目名称', label='项目名称', max_length=200, validators=[<UniqueValidator(queryset=Projects.objects.all())>])
leader = CharField(help_text='负责人', label='负责人', max_length=50)
tester = CharField(help_text='测试人员', label='测试人员', max_length=50)
programer = CharField(help_text='开发人员', label='开发人员', max_length=50)
publish_app = CharField(help_text='发布应用', label='发布应用', max_length=50)
desc = CharField(allow_blank=True, allow_null=True, help_text='简要描述', label='简要描述', required=False, style={'base_template': 'textarea.html'})

简化序列化器

1
2
3
fields = ('id', 'name', 'leader', 'tester', 'programer', 'publish_app')
exclude = ('publish_app')
read_only_fields = ('leader','tester')
  • fields:指定显示哪些

  • exclude:指定排除哪些

  • read_only_fields:指定read_only=True的字段

如果需要修改name的校验,自定义一个name字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ProjectModelSerializer(serializers.ModelSerializer):
name = serializers.CharField(
label='项目名称',
max_length=200,
help_text='项目名称',
validators=[
UniqueValidator(
queryset=Projects.objects.all(),
message='项目名不能重复'
), is_unique_project_name
],
error_messages={'max_length': '长度不能超过200个字节'}
)

class Meta:
model = Projects
fields = '__all__'
read_only_fields = ('leader', 'tester')

也可以增加extra_kwargs增加其他内容

1
2
3
4
5
6
7
8
9
10
11
class Meta:
# 指定参考哪一个模型类来创建
model = Projects
# 指定为模型类的哪些字段,来生成序列化器
fields = '__all__'
extra_kwargs = {
'leader': {
'write_only': True,
'error_messages': {'max_length': '长度不能超过50个字节'}
}
}

测试一下:

1
2
3
4
5
6
7
8
9
10
Out[3]: 
ProjectModelSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(error_messages={'max_length': '长度不能超过200个字节'}, help_text='项目名称', label='项目名称', max_length=200, validators=[<UniqueValidator(queryset=<QuerySet [<Projects: 测试游记>, <Projects: 测试游记1>, <Projects: 「测试游记」-创建>, <Projects: 1015项目>, <Projects: 测试游记项目>]>)>, <function is_unique_project_name>])
leader = CharField(error_messages={'max_length': '长度不能超过50个字节'}, help_text='负责人', label='负责人', max_length=50, write_only=True)
tester = CharField(help_text='测试人员', label='测试人员', max_length=50)
programer = CharField(help_text='开发人员', label='开发人员', max_length=50)
publish_app = CharField(help_text='发布应用', label='发布应用', max_length=50)
desc = CharField(allow_blank=True, allow_null=True, help_text='简要描述', label='简要描述', required=False, style={'base_template': 'textarea.html'})

 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
您的支持将鼓励我继续创作!