测试开发进阶(二十七)

测试开发进阶(二十七)

外键

PrimaryKeyRelatedField

interfaces/serializer.py中创建序列化器

1
2
3
4
5
6
7
8
from rest_framework import serializers
from interfaces.models import Interfaces


class InterfaceModelSerializer(serializers.ModelSerializer):
class Meta:
model = Interfaces
fields = '__all__'

测试

1
2
3
4
5
6
7
8
9
10
11
IN[2]: from interfaces.serializer import InterfaceModelSerializer
IN[3]: InterfaceModelSerializer()

Out[4]:
InterfaceModelSerializer():
id = IntegerField(label='ID', read_only=True)
name = CharField(help_text='项目名称', label='项目名称', max_length=200, validators=[<UniqueValidator(queryset=Interfaces.objects.all())>])
tester = 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'})
project = PrimaryKeyRelatedField(help_text='所属项目', label='所属项目', queryset=Projects.objects.all())

数据库模型中的外键字段默认会生产PrimaryKeyRelatedField序列化器字段

序列化输出的值为外键ID值

序列化输出

1
2
3
4
5
6
7
from interfaces.models import Interfaces
one_interface = Interfaces.objects.get(id=1)
one_interface
Out[7]: <Interfaces: Interfaces object (1)>
interface_serializer = InterfaceModelSerializer(one_interface)
interface_serializer.data
Out[9]: {'id': 1, 'name': '登录接口', 'tester': 'zx', 'desc': '66', 'project': 1}

序列化输出

数据库

字符串关联字段StringRelatedField

重写project

1
project = serializers.StringRelatedField(label='所属项目')
1
2
3
4
5
6
from interfaces.models import Interfaces
from interfaces.serializer import InterfaceModelSerializer
one = Interfaces.objects.get(id=1)
one_s = InterfaceModelSerializer(one)
one_s.data
Out[6]: {'id': 1, 'project': '测试游记', 'name': '登录接口', 'tester': 'zx', 'desc': '66'}

字符串关联字段

StringRelatedField 此字段将被序列化为关联对象字符串表达形式(__str__方法返回值)

1
project = serializers.SlugRelatedField(slug_field='tester')

指定字段数据SlugRelatedField

SlugRelatedField 此字段被序列化为关联对象的指定字段数据

1
project = serializers.SlugRelatedField(slug_field='name',read_only=True)
1
2
3
4
5
6
from interfaces.serializer import InterfaceModelSerializer
from interfaces.models import Interfaces
one = Interfaces.objects.get(id=1)
one_s = InterfaceModelSerializer(one)
one_s.data
Out[6]: {'id': 1, 'project': '测试游记', 'name': '登录接口', 'tester': 'zx', 'desc': '66'}

SlugRelatedField

关联对象的序列化器

1
2
from projects.serializer import ProjectModelSerializer
project = ProjectModelSerializer(label='所属项目', read_only=True)
1
2
3
4
5
6
from interfaces.serializer import InterfaceModelSerializer
from interfaces.models import Interfaces
one = Interfaces.objects.get(id=1)
one_s = InterfaceModelSerializer(one)
one_s.data
Out[6]: {'id': 1, 'project': OrderedDict([('id', 1), ('name', '测试游记'), ('tester', 'zx'), ('programer', 'zhong'), ('publish_app', '公众号'), ('desc', '666')]), 'name': '登录接口', 'tester': 'zx', 'desc': '66'}

对象序列化器

反向指定

父表中默认不会生产关联字段(从表),可以手动指定,字段名默认为子表模型类名「小写_set」

projects.serializer.ProjectModelSerializer中添加

1
interfaces_set = serializers.StringRelatedField(many=True)
1
2
3
4
5
from projects.serializer import ProjectModelSerializer
from projects.models import Projects
p = Projects.objects.get(id=1)
ProjectModelSerializer(p).data
Out[5]: {'id': 1, 'name': '测试游记', 'interfaces_set': ['Interfaces object (1)', 'Interfaces object (2)'], 'tester': 'zx', 'programer': 'zhong', 'publish_app': '公众号', 'desc': '666'}

反向指定

优化视图-请求

1
from rest_framework.views import APIView

当视图继承APIView之后,请求实例方法中的第二个参数requestRequest对象,是对Django中的HttpRequest对象进行的拓展

Request对象

发送json

http :8000/project/ < projects.json

request.data

发送form表单

http -f :8000/project/ < projects.form

1
name=测试游记项目&leader=icon&tester=zhongxin&programer=zhong3&publish_app=公众号&desc=无

request.data

request.POST

Request

  • 对Django中的HttpRequest进行拓展

根据请求头中的Content-Type自动进行解析

无论前端发送那种格式数据,都可以以相同的方式读取

  • request.data

类似于Django中的request.POST和request.FILES

可以对POST,PUT,PATCH的请求体进行解析

支持form表单传参,支持json格式传参

  • request.query_params

类似Django中的request.GET

获取查询字符串参数

  • 支持Django.request中所有的对象和方法

优化视图-渲染

1
from rest_framework.response import Response

LearnDjango/settings.py中添加:

1
2
3
4
5
6
7
8
9
# 指定默认渲染类
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES": (
# json渲染器为第一优先级
"rest_framework.renderers.JSONRenderer",
# 可浏览的API渲染为第二优先级
"rest_framework.renderers.BrowsableAPIRenderer",
)
}

修改projects.views.ProjectDetail#get

1
2
3
4
def get(self, request, pk):
project = self.get_object(pk)
serializer = ProjectSerializer(instance=project)
return Response(serializer.data)

渲染1

http :8000/project/1/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(LearnDjango) zhongxindeMacBook-Pro:apitest zhongxin$ http :8000/project/1/
HTTP/1.1 200 OK
Allow: GET, PUT, DELETE, HEAD, OPTIONS
Content-Length: 120
Content-Type: application/json
Date: Fri, 18 Oct 2019 13:34:44 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Accept, Origin, Cookie
X-Frame-Options: SAMEORIGIN

{
"desc": "666",
"id": 1,
"leader": "zx_94",
"name": "测试游记",
"programer": "zhong",
"publish_app": "公众号",
"tester": "zx"
}

返回json

渲染2

浏览器中输入:http://127.0.0.1:8000/project/1/

返回web界面

Response

参数

  • data

序列化处理后的数据

一般为serializer.data「python基本数据类型:字典,嵌套字典的列表」

  • status

状态码,默认200

  • template_name

模版名称,使用HTMLRenderer渲染时需要指明

  • headers

用于存放响应头信息的字典

  • content_type

响应头中的Content-Type

通常此参数无需设置,会自动根据前端所需类型数据来设置该参数

状态码

1
from rest_framework import status

状态码

修改get

1
2
3
4
def get(self, request, pk):
project = self.get_object(pk)
serializer = ProjectSerializer(instance=project)
return Response(serializer.data, status=status.HTTP_200_OK)

支持分页,排序的父类GenericAPIView

1
from rest_framework.generics import GenericAPIView
  1. 在视图类中指定过滤引擎
  2. 指定需要排序的字段
  3. 指定查询集
  4. 指定模型序列化器
1
2
3
4
5
6
7
8
9
10
class ProjectsList(GenericAPIView):
# 1.在视图类中指定过滤引擎
# OrderingFilter排序
filter_backends = [filters.OrderingFilter]
# 2.指定需要排序的字段
ordering_fields = ['name', 'leader']
# 3.指定查询集
queryset = Projects.objects.all()
# 4.指定模型序列化器
serializer_class = ProjectModelSerializer

查看rest_framework.generics.GenericAPIView#get_queryset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def get_queryset(self):
"""
Get the list of items for this view.
This must be an iterable, and may be a queryset.
Defaults to using `self.queryset`.

This method should always be used rather than accessing `self.queryset`
directly, as `self.queryset` gets evaluated only once, and those results
are cached for all subsequent requests.

You may want to override this if you need to provide different
querysets depending on the incoming request.

(Eg. return a list of items that is specific to the user)
"""
assert self.queryset is not None, (
"'%s' should either include a `queryset` attribute, "
"or override the `get_queryset()` method."
% self.__class__.__name__
)

queryset = self.queryset
if isinstance(queryset, QuerySet):
# Ensure queryset is re-evaluated on each request.
queryset = queryset.all()
return queryset

所以:project_qs = Projects.objects.all()project_qs = self.get_queryset()一致

查看rest_framework.generics.GenericAPIView#get_serializerrest_framework.generics.GenericAPIView#get_serializer_class

1
2
3
4
5
6
7
8
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def get_serializer_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`.

You may want to override this if you need to provide different
serializations depending on the incoming request.

(Eg. admins get full serialization, others get basic serialization)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)

return self.serializer_class

所以:serializer = ProjectSerializer(instance=project_qs, many=True)serializer = self.get_serializer(instance=project_qs, many=True)一致

本轮优化后的视图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from django.http import Http404
from projects.models import Projects
from projects.serializer import ProjectSerializer, ProjectModelSerializer
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework import status, filters


class ProjectsList(GenericAPIView):
# 1.在视图类中指定过滤引擎
# OrderingFilter排序
filter_backends = [filters.OrderingFilter]
# 2.指定需要排序的字段
ordering_fields = ['name', 'leader', 'id']
# 3.指定查询集
queryset = Projects.objects.all()
# 4.指定模型序列化器
serializer_class = ProjectModelSerializer

def get(self, reuqest):
project_qs = self.get_queryset()
project_qs = self.filter_queryset(project_qs)
serializer = self.get_serializer(instance=project_qs, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)

def post(self, request):
serializer = ProjectSerializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
except Exception as e:
return Response(serializer.errors)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)


class ProjectDetail(APIView):
def get_object(self, pk):
try:
return Projects.objects.get(id=pk)
except Projects.DoesNotExist:
raise Http404

def get(self, request, pk):
project = self.get_object(pk)
serializer = ProjectSerializer(instance=project)
return Response(serializer.data, status=status.HTTP_200_OK)

def put(self, request, pk):
project = self.get_object(pk)
serializer = ProjectSerializer(instance=project, data=request.data)
try:
serializer.is_valid(raise_exception=True)
except Exception as e:
return Response(serializer.errors)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

def delete(self, request, pk):
project = self.get_object(pk)
project.delete()
return Response({}, status=status.HTTP_204_NO_CONTENT)

正序

正排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
zhongxindeMacBook-Pro:~ zhongxin$ http :8000/project/?ordering=id
HTTP/1.1 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 711
Content-Type: application/json
Date: Fri, 18 Oct 2019 14:14:51 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Accept, Origin, Cookie
X-Frame-Options: SAMEORIGIN

[
{
"desc": "666",
"id": 1,
"interfaces_set": [
"Interfaces object (1)",
"Interfaces object (2)"
],
"name": "测试游记",
"programer": "zhong",
"publish_app": "公众号",
"tester": "zx"
},
{
"desc": "6666",
"id": 2,
"interfaces_set": [],
"name": "测试游记1",
"programer": "zhong1",
"publish_app": "公众号1",
"tester": "zx1"
},
{
"desc": "666",
"id": 3,
"interfaces_set": [],
"name": "「测试游记」-创建",
"programer": "zx",
"publish_app": "公众号",
"tester": "zx"
},
{
"desc": "「测试游记」",
"id": 7,
"interfaces_set": [],
"name": "1015项目",
"programer": "zhong2",
"publish_app": "公众号2",
"tester": "zx"
},
{
"desc": "无",
"id": 8,
"interfaces_set": [],
"name": "测试游记项目",
"programer": "zhong3",
"publish_app": "公众号",
"tester": "zhongxin"
}
]

zhongxindeMacBook-Pro:~ zhongxin$

倒序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
zhongxindeMacBook-Pro:~ zhongxin$ http :8000/project/?ordering=-id
HTTP/1.1 200 OK
Allow: GET, POST, HEAD, OPTIONS
Content-Length: 711
Content-Type: application/json
Date: Fri, 18 Oct 2019 14:15:51 GMT
Server: WSGIServer/0.2 CPython/3.7.1
Vary: Accept, Origin, Cookie
X-Frame-Options: SAMEORIGIN

[
{
"desc": "无",
"id": 8,
"interfaces_set": [],
"name": "测试游记项目",
"programer": "zhong3",
"publish_app": "公众号",
"tester": "zhongxin"
},
{
"desc": "「测试游记」",
"id": 7,
"interfaces_set": [],
"name": "1015项目",
"programer": "zhong2",
"publish_app": "公众号2",
"tester": "zx"
},
{
"desc": "666",
"id": 3,
"interfaces_set": [],
"name": "「测试游记」-创建",
"programer": "zx",
"publish_app": "公众号",
"tester": "zx"
},
{
"desc": "6666",
"id": 2,
"interfaces_set": [],
"name": "测试游记1",
"programer": "zhong1",
"publish_app": "公众号1",
"tester": "zx1"
},
{
"desc": "666",
"id": 1,
"interfaces_set": [
"Interfaces object (1)",
"Interfaces object (2)"
],
"name": "测试游记",
"programer": "zhong",
"publish_app": "公众号",
"tester": "zx"
}
]

倒排序

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