DRF 框架
DRF 框架知识总览
接口(api)
什么是接口
- 定义
前台与后台进行信息交互的媒介 -- url链接
接口组成
-
url链接 -
http://127.0.0.1:9000/api/users/
-
请求方式 - get(查), post(增), put(整体改), patch(局部改), delete(删)
-
请求参数 - 拼接参数, 数据包参数(urlencoded, json, form-data)
-
响应结果 - 响应的 json数据
-
开发阶段接口测试工具
1
2Postman:
官网下载
接口文档
为什么要写接口文档
- 作为后台开发者, 要通过
url
将数据返回给前台 - 接口文档是给 后台开发者, 前台开发者, 测试等各个相关项目组同事查看的, 方便团队开发(规则是后台指定的, 文档后台来写)
编写文档方式
- word 编写(不推荐)
- drf 框架插件, 可以根据cbv的类快速生产文档(类必须有注释等信息, 要规范, 不推荐)
- 采用写文档的平台
书写过程
- 先按照开发需要,完成接口的开发.
- 设置后台
url
链接 - 设置请求方式
- 设置请求数据
- 设置响应结果
- 设置后台
- 选择接口平台 将以上变成文档即可
接口规范
为什么要制定接口规范
- 在前后台分离的情况下, 后台可以采用不同的后台应用, 因为前后台的响应规则是一致的
- 按照一套规范来编写, 后台不管是什么语言, 前台都可以采用一样的交互方式.
- 与之相反, 后台也不用管前台以何种方式开发
通用的接口规范: Restful 接口规范
-
url 编写
- https 协议 - 保证数据的安全性
- api字眼 - 标识操作的是数据
- v1/v2 字眼 - 数据的多版本共存
- 资源复数 - 请求的数据称之为资源 用数据类别的复数
- 拼接条件 - 过滤群查接口数据 (
https://api.baidu.com/books/?limit=3&ordering=-price
)
-
请求方式
- /books/ - get - 群查
- /books/(pk)/ - get - 单查
- /books/ - post - 单增
- /books/(pk)/ - put - 单整体改
- /books/(pk)/ - patch - 单局部改
- /books/(pk)/ - delete - 单删
-
响应结果
- 网络状态码与状态信息:2xx | 3xx | 4xx | 5xx
- 数据状态码:前后台约定规则 - 0:成功 1:失败 2:成功无结果
- 数据状态信息:自定义成功失败的信息解释(英文)
- 数据本体:json数据
- 数据子资源:头像、视频等,用资源的url链接
-
基于restful接口规范的接口设计
1
2
3
4
5urlpatterns = [
# 资源books接口的设计
url(r'^books/$', views.BookAPIView.as_view()), # 群查、单增
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()), # 单查、单删、单(整体|局部)改
]
FBV 与 CBV : Function|Class Base View
- CBV 比FBV的优势
- fbv没一个接口都会对应一个函数来响应请求
- cbv可以将一个资源的增删改查所有操放在一个类中管理,在内部再分方法逐一处理 (高内聚低耦合:六个接口和一个类有关,但都能在类内部处理)
DRF 框架的基础视图类
APIView
请求模块
DRF 框架APIView 的请求生命周期 概览
重写as_view方法
-
as_view方法完成路由配置,返回配置函数是 csrf_exempt(view),也就是禁用了csrf认证规则
-
所有继承APIView的子类,都不受csrf认证规则的限制
-
将请求处理的任务交给dispath方法完成
解析模块
-
二次封装了原生Django的uwsgi协议的request对象,并做了向下兼容(原来request对象的内容,用现在的request对象都能访问)
-
将所有拼接参数都放在request.query_params中,将所有数据包参数都放在request.data中
-
路由的有名无名分组的数据还是保存在args和kwargs中
-
解析模块在 setting.py自定义解析设置
1
2
3
4
5
6
7
8REST_FRAMEWORK = {
# 解析模块
'DEFAULT_PARSER_CLASSES': [
'rest_framework.parsers.JSONParser', # json
'rest_framework.parsers.FormParser', # urlencoded
'rest_framework.parsers.MultiPartParser' # form-data
],
}
渲染模块
-
当三大认证模块和自己处理请求的视图逻辑没有出现异常时,会执行响应渲染模块
-
响应的数据会交给渲染模块来完成数据的渲染,渲染方式有两种:Json格式数据渲染、Brower格式数据渲染
-
自定义渲染模块解析配置
1
2
3
4
5
6
7
8REST_FRAMEWORK = {
# 渲染模块
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
# 浏览器渲染,上线后会注释,不然浏览器请求接口就暴露了后台语言
'rest_framework.renderers.BrowsableAPIRenderer',
],
}
响应模块
响应类: Response
-
参数
1
2
3
4
5
6
7
8
9
10
11"""
def __init__(self, data=None, status=None, template_name=None, headers=None, exception=False, content_type=None):
pass
data:响应的数据 - 空、字符串、数字、列表、字段、布尔
status:网络状态码
template_name:drf说自己也可以支持前后台不分离返回页面,但是不能和data共存(不会涉及)
headers:响应头(不用刻意去管)
exception:是否是异常响应(如果是异常响应,可以赋值True,没什么用)
content_type:响应的结果类型(如果是响应data,默认就是application/json,所有不用管)
""" -
常见使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16Response(
data={
'status': 0,
'msg': 'ok',
'result': '正常数据'
}
)
Response(
data={
'status': 1,
'msg': '客户端错误提示',
},
status=status.HTTP_400_BAD_REQUEST,
exception=True
)
异常模块
DRF 核心组件
序列组件
序列化基类 BaseSerializer
-
初始化参数
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
26def __init__(self, instance=None, data=empty, **kwargs):
# instance: 对象类型数据赋值给instance形参
# data:请求来的数据赋值给data 形参
# kwargs:内部有三个属性:many、partial、context
# many:操作的对象或数据,是单个的还是多个的
# partial:在修改需求时使用,可以将所有校验字段required校验规则设置为False
# context:用于视图类和序列化类直接传参使用
# 常见使用
# 单查接口
UserModelSerializer(instance=user_obj)
# 群查接口
UserModelSerializer(instance=user_query, many=True)
# 增接口
UserModelSerializer(data=request.data)
# 整体改接口
UserModelSerializer(instance=user_obj, data=request.data)
# 局部改接口
UserModelSerializer(instance=user_obj, data=request.data, partial=True)
# 删接口,用不到序列化类
单表序列化
-
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24from django.db import models
from django.conf import settings
class User(models.Model):
SEX_CHOICES = ((0, '男'), (1, '女'))
name = models.CharField(max_length=64, verbose_name='姓名')
password = models.CharField(max_length=64)
age = models.IntegerField()
height = models.DecimalField(max_digits=5, decimal_places=2, default=0)
sex = models.IntegerField(choices=SEX_CHOICES, default=0)
# sex = models.CharField(choices=[('0', '男'), ('1', '女')])
icon = models.ImageField(upload_to='icon', default='icon/default.png')
# 自定义序列化给前台的字段
# 优点:1)可以格式化数据库原有字段的数据 2)可以对外隐藏原有数据库字段名 3)可以直接连表操作 -
serializers.py
1
2
3
4
5
6
7
8
9
10from rest_framework import serializers
from . import models
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
# 该序列化类是辅助于那个Model类的
model = models.User
# 设置参与序列化与反序列化字段
# 插拔式:可以选择性返回给前台字段(插头都是在Model类中制造)
# fields = ['name', 'age', 'height', 'sex', 'icon]
fields = ['name', 'age', 'height', 'gender', 'img'] -
views.py
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
33from rest_framework.views import APIView
from rest_framework.response import Response
from . import models, serializers
class UserAPIView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk: # 单查
# 1)数据库交互拿到资源obj或资源objs
# 2)数据序列化成可以返回给前台的json数据
# 3)将json数据返回给前台
obj = models.User.objects.get(pk=pk)
serializer = serializers.UserModelSerializer(obj, many=False)
return Response(serializer.data)
else: # 群查
# 1)数据库交互拿到资源obj或资源objs
# 2)数据序列化成可以返回给前台的json数据
# 3)将json数据返回给前台
queryset = models.User.objects.all()
# many操作的数据是否是多个
serializer = serializers.UserModelSerializer(queryset, many=True)
return Response(serializer.data)
def post(self, request, *args, **kwargs):
# 单增
# 1)从请求request中获得前台提交的数据
# 2)将数据转换成Model对象,并完成数据库入库操作
# 3)将入库成功的对象列化成可以返回给前台的json数据(请求与响应数据不对等:请求需要提交密码,响应一定不展示密码)
# 4)将json数据返回给前台
return Response()
单表反序列化
-
views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class UserAPIView(APIView):
def post(self, request, *args, **kwargs):
# 单增
# 1)将前台请求的数据交给序列化类处理
# 2)序列化类执行校验方法,对前台提交的所有数据进行数据校验:校验失败就是异常返回,成功才能继续
# 3)序列化组件完成数据入库操作,得到入库对象
# 4)响应结果给前台
serializer = serializers.UserModelSerializer(data=request.data)
if serializer.is_valid():
# 校验成功 => 入库 => 正常响应
obj = serializer.save()
return Response({
'status': 0,
'msg': 'ok',
'result': '新增的那个对象'
}, status=status.HTTP_201_CREATED)
else:
# 校验失败 => 异常响应
return Response({
'status': 1,
'msg': serializer.errors,
}, status=status.HTTP_400_BAD_REQUEST) -
反序列化
serializers.py
-
1: 不管是序列化还是反序列化字段,都必须在fields中进行声明,没有声明的不会参与任何过程(数据都会被丢弃)
-
2: 用 read_only 表示只读,用 write_only 表示只写,不标注两者,代表可读可写
-
3: 自定义只读字段,在model类中用@property声明,默认就是read_only
1
2
3 -
4: 自定义只写字段,在serializer类中声明,必须手动明确write_only
1
re_password = serializers.CharField(write_only=True)
-
特殊)在serializer类中声明,没有明确write_only,是对model原有字段的覆盖,且可读可写
password = serializers.CharField() -
5: 用 extra_kwargs 来为 写字段 制定基础校验规则(了解)
-
6: 每一个 写字段 都可以用局部钩子 validate_字段(self, value) 方法来自定义校验规则,成功返回value,失败抛出 exceptions.ValidationError(‘异常信息’) 异常
-
7: 需要联合校验的 写字段们,用 validate(self, attrs) 方法来自定义校验规则,,成功返回attrs,失败抛出 exceptions.ValidationError({‘异常字段’: ‘异常信息’}) 异常
-
8: extra_kwargs中一些重要的限制条件
i)'required':代表是否必须参与写操作,有默认值或可以为空的字段,该值为False;反之该值为True;也可以手动修改值
-
序列化类其他配置
1 |
class AuthorModelSerializer(serializers.ModelSerializer): |
子序列化
1 |
""" |
-
url.py
1
2url(r'^authors/$', views.AuthorAPIView.as_view()),
url(r'^authors/(?P<pk>\d+)/$', views.AuthorAPIView.as_view()), -
serializers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23from rest_framework import serializers
from . import models
class AuthorDetailModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.AuthorDetail
fields = ['phone']
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ['name', 'price']
class AuthorModelSerializer(serializers.ModelSerializer):
# 子序列化:子序列化类必须写在上方,且只能对 外键(正向反向)字段 进行覆盖
# 注:运用了子序列化的外键字段,就不能进行数据库的反序列化过程
detail = AuthorDetailModelSerializer(many=False, read_only=True)
books = BookModelSerializer(many=True, read_only=True)
# 问题:
# 1)不设置read_only时,就相当于允许反序列化,反序列化是就会报错
# 2)设置read_only时,可以完成反序列化,但是新增的数据再序列化了,就没有外键关联的数据,与原来数据格式就不一致了
class Meta:
model = models.Author
fields = ['name', 'detail', 'books'] -
views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22# 实际开发,资源的大量操作都是查询操作,只有查需求的资源,可以采用子序列化
class AuthorAPIView(APIView):
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
obj = models.Author.objects.filter(is_delete=False, pk=pk).first()
serializer = serializers.AuthorModelSerializer(instance=obj)
return APIResponse(result=serializer.data)
else:
queryset = models.Author.objects.filter(is_delete=False).all()
serializer = serializers.AuthorModelSerializer(instance=queryset, many=True)
return APIResponse(results=serializer.data)
# 测试子序列化外键字段,不能参与反序列化,因为
def post(self, request, *args, **kwargs):
serializer = serializers.AuthorModelSerializer(data=request.data)
if serializer.is_valid():
obj = serializer.save()
return APIResponse(result=serializers.AuthorModelSerializer(instance=obj).data, http_status=201)
else:
# 校验失败 => 异常响应
return APIResponse(1, serializer.errors, http_status=400)
多表序列化与反序列化
1 |
""" |
-
urls.py
1
2url(r'^books/$', views.BookAPIView.as_view()),
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()), -
models.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class Book(BaseModel):
# ...
-
serializers
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# 辅助序列化类
class AuthorDetailModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.AuthorDetail
fields = ['phone']
# 辅助序列化类
class AuthorModelSerializer(serializers.ModelSerializer):
detail = AuthorDetailModelSerializer(many=False, read_only=True)
class Meta:
model = models.Author
fields = ['name', 'detail']
# 主序列化类
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ('name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list', 'read_author_list')
extra_kwargs = {
'image': {
'read_only': True,
},
'publish': { # 系统原有的外键字段,要留给反序列化过程使用,序列化外键内容,用@property自定义
'write_only': True,
},
'authors': {
'write_only': True,
}
} -
views.py
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# 六个必备接口:单查、群查、单增、单删、单整体改(了解)、单局部改
# 四个额外接口:群增、群删、群整体改、群局部改
class BookAPIView(APIView):
# 单查群查
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
serializer = serializers.BookModelSerializer(instance=obj)
return APIResponse(result=serializer.data)
else:
queryset = models.Book.objects.filter(is_delete=False).all()
serializer = serializers.BookModelSerializer(instance=queryset, many=True)
return APIResponse(results=serializer.data)
# 单增群增
def post(self, request, *args, **kwargs):
# 如何区别单增群增:request.data是{}还是[]
if not isinstance(request.data, list):
# 单增
serializer = serializers.BookModelSerializer(data=request.data)
serializer.is_valid(raise_exception=True) # 如果校验失败,会直接抛异常,返回给前台
obj = serializer.save()
# 为什么要将新增的对象重新序列化给前台:序列化与反序列化数据不对等
return APIResponse(result=serializers.BookModelSerializer(obj).data, http_status=201)
else:
# 群增
serializer = serializers.BookModelSerializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True) # 如果校验失败,会直接抛异常,返回给前台
objs = serializer.save()
# 为什么要将新增的对象重新序列化给前台:序列化与反序列化数据不对等
return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data, http_status=201)
# 友情注释:群增其实是借助了ListSerializer来的create方法完成的
序列化类外键字段的覆盖
1 |
""" |
-
serializers.py
1
2
3
4
5
6
7
8
9
10
11
12class BookModelSerializer(serializers.ModelSerializer):
# 如何覆盖外键字段
# publish = serializers.PrimaryKeyRelatedField(read_only=True) # 只读
# publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all(), write_only=True) # 只写
# publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all()) # 可读可写
publish = serializers.PrimaryKeyRelatedField(queryset=models.Publish.objects.all())
authors = serializers.PrimaryKeyRelatedField(queryset=models.Author.objects.all(), many=True)
class Meta:
model = models.Book
fields = ('name', 'price', 'image', 'publish', 'authors')
十大接口 序列化总结
1 |
""" |
1 |
""" |
-
models.py
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
# 基类:是抽象的(不会完成数据库迁移),目的是提供共有字段的
class BaseModel(models.Model):
is_delete = models.BooleanField(default=False)
updated_time = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True # 必须完成该配置
class Book(BaseModel):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2, null=True)
image = models.ImageField(upload_to='img', default='img/default.png')
publish = models.ForeignKey(to='Publish', related_name='books', db_constraint=False, on_delete=models.DO_NOTHING)
authors = models.ManyToManyField(to='Author', related_name='books', db_constraint=False)
-
urls.py
1
2
3
url(r'^books/$', views.BookAPIView.as_view()),
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()), -
serializers.py
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
46from rest_framework import serializers
from . import models
# 群增群改辅助类(了解)
class BookListSerializer(serializers.ListSerializer):
"""
1)create群增方法不需要重新
2)update群改方法需要重写,且需要和views中处理request.data的逻辑配套使用
3)self.child就代表该ListSerializer类绑定的ModelSerializer类
BookListSerializer的self.child就是BookModelSerializer
"""
# 重新update方法
def update(self, queryset, validated_data_list):
return [
self.child.update(queryset[index], validated_data) for index, validated_data in enumerate(validated_data_list)
]
# 主序列化类
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
# 配置自定义群增群改序列化类
list_serializer_class = BookListSerializer
model = models.Book
fields = ('name', 'price', 'image', 'publish', 'authors', 'publish_name', 'author_list')
# fields = ('name', 'price', 'image', 'publish', 'authors', 'abc')
extra_kwargs = {
'image': {
'read_only': True,
},
'publish': { # 系统原有的外键字段,要留给反序列化过程使用,序列化外键内容,用@property自定义
'write_only': True,
},
'authors': {
'write_only': True,
},
}
# 需求:内外传参
# 1)在钩子函数中,获得请求请求对象request 视图类 => 序列化类
# 2)序列化钩子校验过程中,也会产生一些数据,这些数据能不能传递给外界使用:序列化类 => 视图类
# 序列化类的context属性,被视图类与序列化类共享
def validate(self, attrs):
print(self.context) # 可以获得视图类在初始化序列化对象时传入的context
# self.context.update({'a': 10}) # 序列化类内部更新context,传递给视图类
return attrs -
views.py
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145from rest_framework.views import APIView
from . import models, serializers
from .response import APIResponse
# 六个必备接口:单查、群查、单增、单删、单整体改(了解)、单局部改
# 四个额外接口:群增、群删、群整体改、群局部改
class BookAPIView(APIView):
# 单查群查
"""
单查:接口:/books/(pk)/
群查:接口:/books/
"""
def get(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
obj = models.Book.objects.filter(is_delete=False, pk=pk).first()
serializer = serializers.BookModelSerializer(instance=obj)
return APIResponse(result=serializer.data)
else:
queryset = models.Book.objects.filter(is_delete=False).all()
serializer = serializers.BookModelSerializer(instance=queryset, many=True)
return APIResponse(results=serializer.data)
# 单增群增
"""
单增:接口:/books/ 数据:dict
群增:接口:/books/ 数据:list
"""
def post(self, request, *args, **kwargs):
# 如何区别单增群增:request.data是{}还是[]
if not isinstance(request.data, list):
# 单增
serializer = serializers.BookModelSerializer(data=request.data)
serializer.is_valid(raise_exception=True) # 如果校验失败,会直接抛异常,返回给前台
obj = serializer.save()
# 为什么要将新增的对象重新序列化给前台:序列化与反序列化数据不对等
return APIResponse(result=serializers.BookModelSerializer(obj).data, http_status=201)
else:
# 群增
serializer = serializers.BookModelSerializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True) # 如果校验失败,会直接抛异常,返回给前台
objs = serializer.save()
# 为什么要将新增的对象重新序列化给前台:序列化与反序列化数据不对等
return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data, http_status=201)
# 单删群删
"""
单删:接口:/books/(pk)/
群删:接口:/books/ 数据:[pk1, ..., pkn]
"""
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk:
pks = [pk] # 将单删伪装成群删一条
else:
pks = request.data # 群删的数据就是群删的主键们
try: # request.data可能提交的是乱七八糟的数据,所以orm操作可能会异常
rows = models.Book.objects.filter(is_delete=False, pk__in=pks).update(is_delete=True)
except:
return APIResponse(1, '数据有误')
if rows: # 只要有受影响的行,就代表删除成功
return APIResponse(0, '删除成功')
return APIResponse(2, '删除失败')
# 单整体改群整体改
"""
单整体改:接口:/books/(pk)/ 数据:dict
群整体改:接口:/books/ 数据:[{pk1, ...}, ..., {pkn, ...}] | {pks: [pk1, ..., pkn], data: [{}, ..., {}]}
"""
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
if pk: # 单
try:
instance = models.Book.objects.get(is_delete=False, pk=pk)
except:
return APIResponse(1, 'pk error', http_status=400)
# 序列化类同时赋值instance和data,代表用data重新更新instance => 修改
serializer = serializers.BookModelSerializer(instance=instance, data=request.data)
serializer.is_valid(raise_exception=True)
obj = serializer.save()
return APIResponse(result=serializers.BookModelSerializer(obj).data)
else: # 群
""" 分析request.data数据 [{'pk':1, 'name': '', 'publish': 1, 'authors': [1, 2]}, ...]
1)从 request.data 中分离出 pks 列表
2)pks中存放的pk在数据库中没有对应数据,或者对应的数据已经被删除了,这些不合理的pk要被剔除
3)pks最终转换得到的 objs 列表长度与 request.data 列表长度不一致,就是数据有误
"""
pks = []
try: # 只要不是要求的标准数据,一定会在下方四行代码某一行抛出异常
for dic in request.data:
pks.append(dic.pop('pk'))
objs = models.Book.objects.filter(is_delete=False, pk__in=pks)
assert len(objs) == len(request.data) # 两个列表长度必须一致
except:
return APIResponse(1, '数据有误', http_status=400)
serializer = serializers.BookModelSerializer(instance=objs, data=request.data, many=True)
serializer.is_valid(raise_exception=True)
objs = serializer.save()
return APIResponse(result=serializers.BookModelSerializer(objs, many=True).data)
# 单局部改群局部改
"""