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.py1
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
1re_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.py1
2url(r'^authors/$', views.AuthorAPIView.as_view()),
url(r'^authors/(?P<pk>\d+)/$', views.AuthorAPIView.as_view()), -
serializers.py1
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.py1
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.py1
2url(r'^books/$', views.BookAPIView.as_view()),
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()), -
models.py1
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):
# ...
-
serializers1
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.py1
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.py1
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.py1
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.py1
2
3
url(r'^books/$', views.BookAPIView.as_view()),
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view()), -
serializers.py1
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.py1
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)
# 單局部改群局部改
"""
