Django REST framework
1.引入Django REST framework
1.1使用Django開發(fā)REST 接口
我們以在Django框架中使用的圖書英雄案例來寫一套支持圖書數(shù)據(jù)增刪改查的REST API接口,來理解REST API的開發(fā)。
在此案例中,前后端均發(fā)送JSON格式數(shù)據(jù)。
# views.py
from datetime import datetime
class BooksAPIVIew(View):
"""
查詢所有圖書、增加圖書
"""
def get(self, request):
"""
查詢所有圖書
路由:GET /books/
"""
queryset = BookInfo.objects.all()
book_list = []
for book in queryset:
book_list.append({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
return JsonResponse(book_list, safe=False)
def post(self, request):
"""
新增圖書
路由:POST /books/
"""
json_bytes = request.body
json_str = json_bytes.decode()
book_dict = json.loads(json_str)
book = BookInfo.objects.create(
btitle=book_dict.get('btitle'),
bpub_date=datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()
)
return JsonResponse({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
}, status=201)
class BookAPIView(View):
def get(self, request, pk):
"""
獲取單個(gè)圖書信息
路由: GET /books/<pk>/
"""
try:
book = BookInfo.objects.get(pk=pk)
except BookInfo.DoesNotExist:
return HttpResponse(status=404)
return JsonResponse({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
def put(self, request, pk):
"""
修改圖書信息
路由: PUT /books/<pk>
"""
try:
book = BookInfo.objects.get(pk=pk)
except BookInfo.DoesNotExist:
return HttpResponse(status=404)
json_bytes = request.body
json_str = json_bytes.decode()
book_dict = json.loads(json_str)
book.btitle = book_dict.get('btitle')
book.bpub_date = datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()
book.save()
return JsonResponse({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
def delete(self, request, pk):
"""
刪除圖書
路由: DELETE /books/<pk>/
"""
try:
book = BookInfo.objects.get(pk=pk)
except BookInfo.DoesNotExist:
return HttpResponse(status=404)
book.delete()
return HttpResponse(status=204)
# urls.py
urlpatterns = [
url(r'^books/$', views.BooksAPIVIew.as_view()),
url(r'^books/(?P<pk>\d+)/$', views.BookAPIView.as_view())
]
測試
使用Postman測試上述接口
1) 獲取所有圖書數(shù)據(jù)
GET 方式訪問 http://127.0.0.1:8000/books/, 返回狀態(tài)碼200,數(shù)據(jù)如下
[
{
"id": 1,
"btitle": "射雕英雄傳",
"bpub_date": "1980-05-01",
"bread": 12,
"bcomment": 34,
"image": ""
},
{
"id": 2,
"btitle": "天龍八部",
"bpub_date": "1986-07-24",
"bread": 36,
"bcomment": 40,
"image": ""
},
{
"id": 3,
"btitle": "笑傲江湖",
"bpub_date": "1995-12-24",
"bread": 20,
"bcomment": 80,
"image": ""
},
{
"id": 4,
"btitle": "雪山飛狐",
"bpub_date": "1987-11-11",
"bread": 58,
"bcomment": 24,
"image": ""
},
{
"id": 5,
"btitle": "西游記",
"bpub_date": "1988-01-01",
"bread": 10,
"bcomment": 10,
"image": "booktest/xiyouji.png"
},
{
"id": 6,
"btitle": "水滸傳",
"bpub_date": "1992-01-01",
"bread": 10,
"bcomment": 11,
"image": ""
},
{
"id": 7,
"btitle": "紅樓夢",
"bpub_date": "1990-01-01",
"bread": 0,
"bcomment": 0,
"image": ""
}
]
2)獲取單一圖書數(shù)據(jù)
GET 訪問 http://127.0.0.1:8000/books/5/ ,返回狀態(tài)碼200, 數(shù)據(jù)如下
{
"id": 5,
"btitle": "西游記",
"bpub_date": "1988-01-01",
"bread": 10,
"bcomment": 10,
"image": "booktest/xiyouji.png"
}
GET 訪問http://127.0.0.1:8000/books/100/,返回狀態(tài)碼404
3)新增圖書數(shù)據(jù)
POST 訪問http://127.0.0.1:8000/books/,發(fā)送JSON數(shù)據(jù):
{
"btitle": "三國演義",
"bpub_date": "1990-02-03"
}
返回狀態(tài)碼201,數(shù)據(jù)如下
{
"id": 8,
"btitle": "三國演義",
"bpub_date": "1990-02-03",
"bread": 0,
"bcomment": 0,
"image": ""
}
4)修改圖書數(shù)據(jù)
PUT 訪問http://127.0.0.1:8000/books/8/,發(fā)送JSON數(shù)據(jù):
{
"btitle": "三國演義(第二版)",
"bpub_date": "1990-02-03"
}
返回狀態(tài)碼200,數(shù)據(jù)如下
{
"id": 8,
"btitle": "三國演義(第二版)",
"bpub_date": "1990-02-03",
"bread": 0,
"bcomment": 0,
"image": ""
}
5)刪除圖書數(shù)據(jù)
DELETE 訪問http://127.0.0.1:8000/books/8/,返回204狀態(tài)碼
1.2 明確REST接口開發(fā)的核心任務(wù)
分析一下上節(jié)的案例,可以發(fā)現(xiàn),在開發(fā)REST API接口時(shí),視圖中做的最主要有三件事:
- 將請求的數(shù)據(jù)(如JSON格式)轉(zhuǎn)換為模型類對象
- 操作數(shù)據(jù)庫
- 將模型類對象轉(zhuǎn)換為響應(yīng)的數(shù)據(jù)(如JSON格式)
序列化Serialization
序列化理解為:將程序中的一個(gè)數(shù)據(jù)結(jié)構(gòu)類型轉(zhuǎn)換為其他格式(字典、JSON、XML等),例如將Django中的模型類對象裝換為JSON字符串,這個(gè)轉(zhuǎn)換過程我們稱為序列化。
如:
queryset = BookInfo.objects.all()
book_list = []
# 序列化
for book in queryset:
book_list.append({
'id': book.id,
'btitle': book.btitle,
'bpub_date': book.bpub_date,
'bread': book.bread,
'bcomment': book.bcomment,
'image': book.image.url if book.image else ''
})
return JsonResponse(book_list, safe=False)
反之,將其他格式(字典、JSON、XML等)轉(zhuǎn)換為程序中的數(shù)據(jù),例如將JSON字符串轉(zhuǎn)換為Django中的模型類對象,這個(gè)過程我們稱為反序列化。
如:
json_bytes = request.body
json_str = json_bytes.decode()
# 反序列化
book_dict = json.loads(json_str)
book = BookInfo.objects.create(
btitle=book_dict.get('btitle'),
bpub_date=datetime.strptime(book_dict.get('bpub_date'), '%Y-%m-%d').date()
)
我們可以看到,在開發(fā)REST API時(shí),視圖中要頻繁的進(jìn)行序列化與反序列化的編寫。
總結(jié)
在開發(fā)REST API接口時(shí),我們在視圖中需要做的最核心的事是:
-
將數(shù)據(jù)庫數(shù)據(jù)序列化為前端所需要的格式,并返回;
-
將前端發(fā)送的數(shù)據(jù)反序列化為模型類對象,并保存到數(shù)據(jù)庫中。
1.3Django REST framework 簡介
- 在序列化與反序列化時(shí),雖然操作的數(shù)據(jù)不盡相同,但是執(zhí)行的過程卻是相似的,也就是說這部分代碼是可以復(fù)用簡化編寫的。
- 在開發(fā)REST API的視圖中,雖然每個(gè)視圖具體操作的數(shù)據(jù)不同,但增、刪、改、查的實(shí)現(xiàn)流程基本套路化,所以這部分代碼也是可以復(fù)用簡化編寫的:
- 增:校驗(yàn)請求數(shù)據(jù) -> 執(zhí)行反序列化過程 -> 保存數(shù)據(jù)庫 -> 將保存的對象序列化并返回
- 刪:判斷要?jiǎng)h除的數(shù)據(jù)是否存在 -> 執(zhí)行數(shù)據(jù)庫刪除
- 改:判斷要修改的數(shù)據(jù)是否存在 -> 校驗(yàn)請求的數(shù)據(jù) -> 執(zhí)行反序列化過程 -> 保存數(shù)據(jù)庫 -> 將保存的對象序列化并返回
- 查:查詢數(shù)據(jù)庫 -> 將數(shù)據(jù)序列化并返回
Django REST framework可以幫助我們簡化上述兩部分的代碼編寫,大大提高REST API的開發(fā)速度。
認(rèn)識(shí)Django REST framework
Django REST framework 框架是一個(gè)用于構(gòu)建Web API 的強(qiáng)大而又靈活的工具。
通常簡稱為DRF框架 或 REST framework。
DRF框架是建立在Django框架基礎(chǔ)之上,由Tom Christie大牛二次開發(fā)的開源項(xiàng)目。
特點(diǎn)
- 提供了定義序列化器Serializer的方法,可以快速根據(jù) Django ORM 或者其它庫自動(dòng)序列化/反序列化;
- 提供了豐富的類視圖、Mixin擴(kuò)展類,簡化視圖的編寫;
- 豐富的定制層級(jí):函數(shù)視圖、類視圖、視圖集合到自動(dòng)生成 API,滿足各種需要;
- 多種身份認(rèn)證和權(quán)限認(rèn)證方式的支持;
- 內(nèi)置了限流系統(tǒng);
- 直觀的 API web 界面;
- 可擴(kuò)展性,插件豐富
2. DRF工程搭建
2.1 環(huán)境安裝與配置
DRF需要以下依賴:
- Python (3.6,3.7,3.8,3.9)
- Django (2.0,3.0,3.2.11)
DRF是以Django擴(kuò)展應(yīng)用的方式提供的,所以我們可以直接利用已有的Django環(huán)境而無需從新創(chuàng)建。(若沒有Django環(huán)境,需要先創(chuàng)建環(huán)境安裝Django)
1. 安裝DRF
pip install djangorestframework
2. 添加rest_framework應(yīng)用
我們利用在Django框架學(xué)習(xí)中創(chuàng)建的demo工程,在settings.py的INSTALLED_APPS中添加'rest_framework'。
INSTALLED_APPS = [
...
'rest_framework',
]
接下來就可以使用DRF進(jìn)行開發(fā)了。
2.2 見識(shí)DRF的魅力
1. 創(chuàng)建序列化器
在booktest應(yīng)用中新建serializers.py用于保存該應(yīng)用的序列化器。
創(chuàng)建一個(gè)BookInfoSerializer用于序列化與反序列化。
class BookInfoSerializer(serializers.ModelSerializer):
"""圖書數(shù)據(jù)序列化器"""
class Meta:
model = BookInfo
fields = '__all__'
- model 指明該序列化器處理的數(shù)據(jù)字段從模型類BookInfo參考生成
- fields 指明該序列化器包含模型類中的哪些字段,'__all__'指明包含所有字段
2. 編寫視圖
在booktest應(yīng)用的views.py中創(chuàng)建視圖BookInfoViewSet,這是一個(gè)視圖集合。
from rest_framework.viewsets import ModelViewSet
from .serializers import BookInfoSerializer
from .models import BookInfo
class BookInfoViewSet(ModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
- queryset 指明該視圖集在查詢數(shù)據(jù)時(shí)使用的查詢集
- serializer_class 指明該視圖在進(jìn)行序列化或反序列化時(shí)使用的序列化器
3. 定義路由
在booktest應(yīng)用的urls.py中定義路由信息。
from . import views
from rest_framework.routers import DefaultRouter
urlpatterns = [
...
]
router = DefaultRouter() # 可以處理視圖的路由器
router.register(r'books', views.BookInfoViewSet) # 向路由器中注冊視圖集
urlpatterns += router.urls # 將路由器中的所以路由信息追到到django的路由列表中
4. 運(yùn)行測試
運(yùn)行當(dāng)前程序(與運(yùn)行Django一樣)
python manage.py runserver
在瀏覽器中輸入網(wǎng)址127.0.0.1:8000,可以看到DRF提供的API Web瀏覽頁面:

1)點(diǎn)擊鏈接127.0.0.1:8000/books/ 可以訪問獲取所有數(shù)據(jù)的接口,呈現(xiàn)如下頁面:


2)在頁面底下表單部分填寫圖書信息,可以訪問添加新圖書的接口,保存新書:

點(diǎn)擊POST后,返回如下頁面信息:

3)在瀏覽器中輸入網(wǎng)址127.0.0.1:8000/books/1/,可以訪問獲取單一圖書信息的接口(id為1的圖書),呈現(xiàn)如下頁面:

4)在頁面底部表單中填寫圖書信息,可以訪問修改圖書的接口:

點(diǎn)擊PUT,返回如下頁面信息:

5)點(diǎn)擊DELETE按鈕,可以訪問刪除圖書的接口:

返回,如下頁面:

3.Serializer序列化器
序列化器的作用:
- 進(jìn)行數(shù)據(jù)的校驗(yàn)
- 對數(shù)據(jù)對象進(jìn)行轉(zhuǎn)換
3.1 定義方法
Django REST framework中的Serializer使用類來定義,須繼承自rest_framework.serializers.Serializer。
例如,我們已有了一個(gè)數(shù)據(jù)庫模型類BookInfo
class BookInfo(models.Model):
btitle = models.CharField(max_length = 20 , verbose_name = '名稱' )
bpub_date = models.DateField(verbose_name = '發(fā)布日期' , null = True )
bread = models.IntegerField(default = 0 , verbose_name = '閱讀量' )
bcomment = models.IntegerField(default = 0 , verbose_name = '評(píng)論量' )
image = models.ImageField(upload_to = 'booktest' , verbose_name = '圖片' , null = True )
為這個(gè)模型類提供一個(gè)序列化器,可以定義如下:
class BookInfoSerializer(serializers.Serializer):
"""圖書數(shù)據(jù)序列化器"""
id = serializers.IntegerField(label = 'ID' , read_only = True )
btitle = serializers.CharField(label = '名稱' , max_length = 20 )
bpub_date = serializers.DateField(label = '發(fā)布日期' , required = False )
bread = serializers.IntegerField(label = '閱讀量' , required = False )
bcomment = serializers.IntegerField(label = '評(píng)論量' , required = False )
image = serializers.ImageField(label = '圖片' , required = False )
注意:serializer不是只能為數(shù)據(jù)庫模型類定義,也可以為非數(shù)據(jù)庫模型類的數(shù)據(jù)定義。serializer是獨(dú)立于數(shù)據(jù)庫之外的存在。
2. 字段與選項(xiàng)
| 字段 | 字段構(gòu)造方式 |
|---|---|
| BooleanField | BooleanField() |
| NullBooleanField | NullBooleanField() |
| CharField | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) |
| EmailField | EmailField(max_length=None, min_length=None, allow_blank=False) |
| RegexField | RegexField(regex, max_length=None, min_length=None, allow_blank=False) |
| SlugField | SlugField(maxlength=50, min_length=None, allow_blank=False) 正則字段,驗(yàn)證正則模式 [a-zA-Z0-9-]+ |
| URLField | URLField(max_length=200, min_length=None, allow_blank=False) |
| UUIDField | UUIDField(format='hex_verbose') format: 1) 'hex_verbose' 如"5ce0e9a5-5ffa-654b-cee0-1238041fb31a"2) 'hex' 如 "5ce0e9a55ffa654bcee01238041fb31a"3) 'int' - 如: "123456789012312313134124512351145145114"4) 'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" |
| IPAddressField | IPAddressField(protocol='both', unpack_ipv4=False, **options) |
| IntegerField | IntegerField(max_value=None, min_value=None) |
| FloatField | FloatField(max_value=None, min_value=None) |
| DecimalField | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位數(shù) decimal_palces: 小數(shù)點(diǎn)位置 |
| DateTimeField | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) |
| DateField | DateField(format=api_settings.DATE_FORMAT, input_formats=None) |
| TimeField | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) |
| DurationField | DurationField() |
| ChoiceField | ChoiceField(choices) choices與Django的用法相同 |
| MultipleChoiceField | MultipleChoiceField(choices) |
| FileField | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
| ImageField | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) |
| ListField | ListField(child=, min_length=None, max_length=None) |
| DictField | DictField(child=) |
選項(xiàng)參數(shù):
| 參數(shù)名稱 | 作用 |
|---|---|
| max_length | 最大長度 |
| min_length | 最小長度 |
| allow_blank | 是否允許為空 |
| trim_whitespace | 是否截?cái)嗫瞻鬃址?/td> |
| max_value | 最大值 |
| min_value | 最小值 |
通用參數(shù):
| 參數(shù)名稱 | 說明 |
|---|---|
| read_only |
表明該字段僅用于序列化輸出,默認(rèn)False 字段只讀,默認(rèn)值為False。只讀字段只會(huì)包含序列化輸出中,創(chuàng)建修改對象不會(huì)包含該字段【設(shè)置序列化字段的只讀屬性】 |
| write_only |
表明該字段僅用于反序列化輸入,默認(rèn)False 字段只寫,默認(rèn)值為False 和 read_only 相反,序列化時(shí)不包含,創(chuàng)建修改對象必須包含該字段【設(shè)置序列化字段的編輯屬性】 |
| required |
表明該字段在反序列化時(shí)必須輸入,默認(rèn)True 字段必須,默認(rèn)是True 必須字段在創(chuàng)建修改對象時(shí)必須包含,否則拋出異常。當(dāng)設(shè)置為False時(shí),在創(chuàng)建修改對象可以不用包含該字段【設(shè)置序列化字段的數(shù)據(jù)是否為空,默認(rèn)值為True】 |
| default |
反序列化時(shí)使用的默認(rèn)值 默認(rèn)值。如果一個(gè)字段設(shè)置了默認(rèn)值,那么在反序列時(shí),如果沒有提供該子彈,則使用默認(rèn)進(jìn)行填充,在進(jìn)行局部更新時(shí),default 不會(huì)應(yīng)用,因?yàn)榫植扛聲r(shí),只有更新的字段會(huì)被校驗(yàn)和返回。還可以設(shè)置一個(gè)函數(shù)或者一個(gè)可調(diào)用對象。可以設(shè)置為一個(gè)函數(shù)或其他可調(diào)用對象,會(huì)使用調(diào)用后的結(jié)果填充。調(diào)用時(shí),它不接受任何參數(shù),如果可調(diào)用對象有一個(gè)requires_context = True屬性,那么序列化字段會(huì)被當(dāng)做參數(shù)傳入 |
| allow_null |
表明該字段是否允許傳入None,默認(rèn)False 如果將None傳遞給序列化器字段,將引發(fā)錯(cuò)誤。如果None應(yīng)該被認(rèn)為是一個(gè)有效值,則將該關(guān)鍵字參數(shù)設(shè)置為True 注意:將該參數(shù)設(shè)置為Ture將意味著序列化輸出的默認(rèn)值是None,但并不意味著輸入反序列化是默認(rèn)的。默認(rèn)值是False |
| validators | 自定義數(shù)據(jù)驗(yàn)證規(guī)則,以列表格式表示,列表元素為數(shù)據(jù)驗(yàn)證的函數(shù)名 |
| error_messages | 包含錯(cuò)誤編號(hào)與錯(cuò)誤信息的字典 |
| label | 一個(gè)文本字符串。可以用作HTML表單字段或其他描述元素的字段名 |
| help_text | 可以在HTML表單子彈活其他描述元素中用作字段描述的文本字符串 |
| source | 將用于填充字段的屬性的名稱。可以是一個(gè)接受self參數(shù)的方法(source='方法名')也可以是屬性(source='channel.name') |
| initial | 設(shè)置序列化的初始值 |
| style | 以字典格式化表示,控制模板引擎如何渲染序列化字段 |
3. 創(chuàng)建Serializer對象
定義好Serializer類后,就可以創(chuàng)建Serializer對象了。
Serializer的構(gòu)造方法為:
Serializer(instance=None, data=empty, **kwarg)
說明:
1)用于序列化時(shí),將模型類對象傳入instance參數(shù)
2)用于反序列化時(shí),將要被反序列化的數(shù)據(jù)傳入data參數(shù)
3.2序列化使用
1 基本使用
1) 先查詢出一個(gè)圖書對象
from booktest.models import BookInfo
book = BookInfo.objects.get(id=2)
2) 構(gòu)造序列化器對象
from booktest.serializers import BookInfoSerializer
serializer = BookInfoSerializer(book)
3)獲取序列化數(shù)據(jù)
通過data屬性可以獲取序列化后的數(shù)據(jù)
serializer.data
# {'id': 2, 'btitle': '天龍八部', 'bpub_date': '1986-07-24', 'bread': 36, 'bcomment': 40, 'image': None}
4)如果要被序列化的是包含多條數(shù)據(jù)的查詢集QuerySet,可以通過添加many=True參數(shù)補(bǔ)充說明
book_qs = BookInfo.objects.all()
serializer = BookInfoSerializer(book_qs, many=True)
serializer.data
# [OrderedDict([('id', 2), ('btitle', '天龍八部'), ('bpub_date', '1986-07-24'), ('bread', 36), ('bcomment', 40), ('image', N]), OrderedDict([('id', 3), ('btitle', '笑傲江湖'), ('bpub_date', '1995-12-24'), ('bread', 20), ('bcomment', 80), ('image'ne)]), OrderedDict([('id', 4), ('btitle', '雪山飛狐'), ('bpub_date', '1987-11-11'), ('bread', 58), ('bcomment', 24), ('ima None)]), OrderedDict([('id', 5), ('btitle', '西游記'), ('bpub_date', '1988-01-01'), ('bread', 10), ('bcomment', 10), ('im', 'booktest/xiyouji.png')])]
2 關(guān)聯(lián)對象嵌套序列化
如果需要序列化的數(shù)據(jù)中包含有其他關(guān)聯(lián)對象,則對關(guān)聯(lián)對象數(shù)據(jù)的序列化需要指明。
例如,在定義英雄數(shù)據(jù)的序列化器時(shí),外鍵hbook(即所屬的圖書)字段如何序列化?
我們先定義HeroInfoSerialzier除外鍵字段外的其他部分
class HeroInfoSerializer(serializers.Serializer):
"""英雄數(shù)據(jù)序列化器"""
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
id = serializers.IntegerField(label='ID', read_only=True)
hname = serializers.CharField(label='名字', max_length=20)
hgender = serializers.ChoiceField(choices=GENDER_CHOICES, label='性別', required=False)
hcomment = serializers.CharField(label='描述信息', max_length=200, required=False, allow_null=True)
對于關(guān)聯(lián)字段,可以采用以下幾種方式:
1) PrimaryKeyRelatedField
此字段將被序列化為關(guān)聯(lián)對象的主鍵。
hbook = serializers.PrimaryKeyRelatedField(label='圖書', read_only=True)
或
hbook = serializers.PrimaryKeyRelatedField(label='圖書', queryset=BookInfo.objects.all())
指明字段時(shí)需要包含read_only=True或者queryset參數(shù):
- 包含read_only=True參數(shù)時(shí),該字段將不能用作反序列化使用
- 包含queryset參數(shù)時(shí),將被用作反序列化時(shí)參數(shù)校驗(yàn)使用
使用效果:
from booktest.serializers import HeroInfoSerializer
from booktest.models import HeroInfo
hero = HeroInfo.objects.get(id=6)
serializer = HeroInfoSerializer(hero)
serializer.data
# {'id': 6, 'hname': '喬峰', 'hgender': 1, 'hcomment': '降龍十八掌', 'hbook': 2}
2)使用關(guān)聯(lián)對象的序列化器
hbook = BookInfoSerializer()
使用效果
{'id': 6, 'hname': '喬峰', 'hgender': 1, 'hcomment': '降龍十八掌', 'hbook': OrderedDict([('id', 2), ('btitle', '天龍八部')te', '1986-07-24'), ('bread', 36), ('bcomment', 40), ('image', None)])}
3) StringRelatedField
此字段將被序列化為關(guān)聯(lián)對象的字符串表示方式(即__str__方法的返回值)
hbook = serializers.StringRelatedField(label='圖書')
使用效果
{'id': 6, 'hname': '喬峰', 'hgender': 1, 'hcomment': '降龍十八掌', 'hbook': '天龍八部'}
4)HyperlinkedRelatedField
此字段將被序列化為獲取關(guān)聯(lián)對象數(shù)據(jù)的接口鏈接
hbook = serializers.HyperlinkedRelatedField(label='圖書', read_only=True, view_name='books-detail')
必須指明view_name參數(shù),以便DRF根據(jù)視圖名稱尋找路由,進(jìn)而拼接成完整URL。
使用效果
{'id': 6, 'hname': '喬峰', 'hgender': 1, 'hcomment': '降龍十八掌', 'hbook': 'http://127.0.0.1:8000/books/2/'}
我們暫時(shí)還沒有定義視圖,此方式不再演示。
5)SlugRelatedField
此字段將被序列化為關(guān)聯(lián)對象的指定字段數(shù)據(jù)
hbook = serializers.SlugRelatedField(label='圖書', read_only=True, slug_field='bpub_date')
slug_field指明使用關(guān)聯(lián)對象的哪個(gè)字段
使用效果
{'id': 6, 'hname': '喬峰', 'hgender': 1, 'hcomment': '降龍十八掌', 'hbook': datetime.date(1986, 7, 24)}
6) 重寫to_representation方法
序列化器的每個(gè)字段實(shí)際都是由該字段類型的to_representation方法決定格式的,可以通過重寫該方法來決定格式。
注意,to_representations方法不僅局限在控制關(guān)聯(lián)對象格式上,適用于各個(gè)序列化器字段類型。
自定義一個(gè)新的關(guān)聯(lián)字段:
class BookRelateField(serializers.RelatedField):
"""自定義用于處理圖書的字段"""
def to_representation(self, value):
return 'Book: %d %s' % (value.id, value.btitle)
指明hbook為BookRelateField類型
hbook = BookRelateField(read_only=True)
使用效果
{'id': 6, 'hname': '喬峰', 'hgender': 1, 'hcomment': '降龍十八掌', 'hbook': 'Book: 2 天龍八部'}
3 many參數(shù)
如果關(guān)聯(lián)的對象數(shù)據(jù)不是只有一個(gè),而是包含多個(gè)數(shù)據(jù),如想序列化圖書BookInfo數(shù)據(jù),每個(gè)BookInfo對象關(guān)聯(lián)的英雄HeroInfo對象可能有多個(gè),此時(shí)關(guān)聯(lián)字段類型的指明仍可使用上述幾種方式,只是在聲明關(guān)聯(lián)字段時(shí),多補(bǔ)充一個(gè)many=True參數(shù)即可。
此處僅拿PrimaryKeyRelatedField類型來舉例,其他相同。
在BookInfoSerializer中添加關(guān)聯(lián)字段:
class BookInfoSerializer(serializers.Serializer):
"""圖書數(shù)據(jù)序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名稱', max_length=20)
bpub_date = serializers.DateField(label='發(fā)布日期', required=False)
bread = serializers.IntegerField(label='閱讀量', required=False)
bcomment = serializers.IntegerField(label='評(píng)論量', required=False)
image = serializers.ImageField(label='圖片', required=False)
heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True) # 新增
使用效果:
from booktest.serializers import BookInfoSerializer
from booktest.models import BookInfo
book = BookInfo.objects.get(id=2)
serializer = BookInfoSerializer(book)
serializer.data
# {'id': 2, 'btitle': '天龍八部', 'bpub_date': '1986-07-24', 'bread': 36, 'bcomment': 40, 'image':
3.3反序列化使用
1. 驗(yàn)證
使用序列化器進(jìn)行反序列化時(shí),需要對數(shù)據(jù)進(jìn)行驗(yàn)證后,才能獲取驗(yàn)證成功的數(shù)據(jù)或保存成模型類對象。
在獲取反序列化的數(shù)據(jù)前,必須調(diào)用is_valid()方法進(jìn)行驗(yàn)證,驗(yàn)證成功返回True,否則返回False。
驗(yàn)證失敗,可以通過序列化器對象的errors屬性獲取錯(cuò)誤信息,返回字典,包含了字段和字段的錯(cuò)誤。如果是非字段錯(cuò)誤,可以通過修改REST framework配置中的NON_FIELD_ERRORS_KEY來控制錯(cuò)誤字典中的鍵名。
驗(yàn)證成功,可以通過序列化器對象的validated_data屬性獲取數(shù)據(jù)。
在定義序列化器時(shí),指明每個(gè)字段的序列化類型和選項(xiàng)參數(shù),本身就是一種驗(yàn)證行為。
如我們前面定義過的BookInfoSerializer
class BookInfoSerializer(serializers.Serializer):
"""圖書數(shù)據(jù)序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名稱', max_length=20)
bpub_date = serializers.DateField(label='發(fā)布日期', required=False)
bread = serializers.IntegerField(label='閱讀量', required=False)
bcomment = serializers.IntegerField(label='評(píng)論量', required=False)
image = serializers.ImageField(label='圖片', required=False)
通過構(gòu)造序列化器對象,并將要反序列化的數(shù)據(jù)傳遞給data構(gòu)造參數(shù),進(jìn)而進(jìn)行驗(yàn)證
from booktest.serializers import BookInfoSerializer
data = {'bpub_date': 123}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # 返回False
serializer.errors
# {'btitle': [ErrorDetail(string='This field is required.', code='required')], 'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data # {}
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # True
serializer.errors # {}
serializer.validated_data # OrderedDict([('btitle', 'python')])
is_valid()方法還可以在驗(yàn)證失敗時(shí)拋出異常serializers.ValidationError,可以通過傳遞raise_exception=True參數(shù)開啟,REST framework接收到此異常,會(huì)向前端返回HTTP 400 Bad Request響應(yīng)。
# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)
如果覺得這些還不夠,需要再補(bǔ)充定義驗(yàn)證行為,可以使用以下三種方法:
1)validators
在字段中添加validators選項(xiàng)參數(shù),也可以補(bǔ)充驗(yàn)證行為,如
def about_django(value):
if 'django' not in value.lower():
raise serializers.ValidationError("圖書不是關(guān)于Django的")
class BookInfoSerializer(serializers.Serializer):
"""圖書數(shù)據(jù)序列化器"""
id = serializers.IntegerField(label='ID', read_only=True)
btitle = serializers.CharField(label='名稱', max_length=20, validators=[about_django])
bpub_date = serializers.DateField(label='發(fā)布日期', required=False)
bread = serializers.IntegerField(label='閱讀量', required=False)
bcomment = serializers.IntegerField(label='評(píng)論量', required=False)
image = serializers.ImageField(label='圖片', required=False)
測試:
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'btitle': [ErrorDetail(string='圖書不是關(guān)于Django的', code='invalid')]}
2)validate_<field_name>
對<field_name>字段進(jìn)行驗(yàn)證,如
class BookInfoSerializer(serializers.Serializer):
"""圖書數(shù)據(jù)序列化器"""
...
def validate_btitle(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("圖書不是關(guān)于Django的")
return value
測試
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # False
serializer.errors
# {'btitle': [ErrorDetail(string='圖書不是關(guān)于Django的', code='invalid')]}
3)validate
在序列化器中需要同時(shí)對多個(gè)字段進(jìn)行比較驗(yàn)證時(shí),可以定義validate方法來驗(yàn)證,如
class BookInfoSerializer(serializers.Serializer):
"""圖書數(shù)據(jù)序列化器"""
...
def validate(self, attrs):
bread = attrs['bread']
bcomment = attrs['bcomment']
if bread < bcomment:
raise serializers.ValidationError('閱讀量小于評(píng)論量')
return attrs
測試
from booktest.serializers import BookInfoSerializer
data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20}
s = BookInfoSerializer(data=data)
s.is_valid() # False
s.errors
# {'non_field_errors': [ErrorDetail(string='閱讀量小于評(píng)論量', code='invalid')]}
REST framework提供的validators:
-
UniqueValidator
單字段唯一,如
from rest_framework.validators import UniqueValidator slug = SlugField( max_length=100, validators=[UniqueValidator(queryset=BlogPost.objects.all())] ) -
UniqueTogetherValidation
聯(lián)合唯一,如
from rest_framework.validators import UniqueTogetherValidator class ExampleSerializer(serializers.Serializer): # ... class Meta: validators = [ UniqueTogetherValidator( queryset=ToDoItem.objects.all(), fields=('list', 'position') ) ]
2. 保存
如果在驗(yàn)證成功后,想要基于validated_data完成數(shù)據(jù)對象的創(chuàng)建,可以通過實(shí)現(xiàn)create()和update()兩個(gè)方法來實(shí)現(xiàn)。
class BookInfoSerializer(serializers.Serializer):
"""圖書數(shù)據(jù)序列化器"""
...
def create(self, validated_data):
"""新建"""
return BookInfo(**validated_data)
def update(self, instance, validated_data):
"""更新,instance為要更新的對象實(shí)例"""
instance.btitle = validated_data.get('btitle', instance.btitle)
instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
instance.bread = validated_data.get('bread', instance.bread)
instance.bcomment = validated_data.get('bcomment', instance.bcomment)
return instance
如果需要在返回?cái)?shù)據(jù)對象的時(shí)候,也將數(shù)據(jù)保存到數(shù)據(jù)庫中,則可以進(jìn)行如下修改
class BookInfoSerializer(serializers.Serializer):
"""圖書數(shù)據(jù)序列化器"""
...
def create(self, validated_data):
"""新建"""
return BookInfo.objects.create(**validated_data)
def update(self, instance, validated_data):
"""更新,instance為要更新的對象實(shí)例"""
instance.btitle = validated_data.get('btitle', instance.btitle)
instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
instance.bread = validated_data.get('bread', instance.bread)
instance.bcomment = validated_data.get('bcomment', instance.bcomment)
instance.save()
return instance
實(shí)現(xiàn)了上述兩個(gè)方法后,在反序列化數(shù)據(jù)的時(shí)候,就可以通過save()方法返回一個(gè)數(shù)據(jù)對象實(shí)例了
book = serializer.save()
如果創(chuàng)建序列化器對象的時(shí)候,沒有傳遞instance實(shí)例,則調(diào)用save()方法的時(shí)候,create()被調(diào)用,相反,如果傳遞了instance實(shí)例,則調(diào)用save()方法的時(shí)候,update()被調(diào)用。
from db.serializers import BookInfoSerializer
data = {'btitle': '封神演義'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid() # True
serializer.save() # <BookInfo: 封神演義>
from db.models import BookInfo
book = BookInfo.objects.get(id=2)
data = {'btitle': '倚天劍'}
serializer = BookInfoSerializer(book, data=data)
serializer.is_valid() # True
serializer.save() # <BookInfo: 倚天劍>
book.btitle # '倚天劍'
兩點(diǎn)說明:
1) 在對序列化器進(jìn)行save()保存時(shí),可以額外傳遞數(shù)據(jù),這些數(shù)據(jù)可以在create()和update()中的validated_data參數(shù)獲取到
serializer.save(owner=request.user)
2)默認(rèn)序列化器必須傳遞所有required的字段,否則會(huì)拋出驗(yàn)證異常。但是我們可以使用partial參數(shù)來允許部分字段更新
# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)
3.4 模型類序列化器ModelSerializer
如果我們想要使用序列化器對應(yīng)的是Django的模型類,DRF為我們提供了ModelSerializer模型類序列化器來幫助我們快速創(chuàng)建一個(gè)Serializer類。
ModelSerializer與常規(guī)的Serializer相同,但提供了:
- 基于模型類自動(dòng)生成一系列字段
- 基于模型類自動(dòng)為Serializer生成validators,比如unique_together
- 包含默認(rèn)的create()和update()的實(shí)現(xiàn)
1. 定義
比如我們創(chuàng)建一個(gè)BookInfoSerializer
class BookInfoSerializer(serializers.ModelSerializer):
"""圖書數(shù)據(jù)序列化器"""
class Meta:
model = BookInfo
fields = '__all__'
- model 指明參照哪個(gè)模型類
- fields 指明為模型類的哪些字段生成
我們可以在python manage.py shell中查看自動(dòng)生成的BookInfoSerializer的具體實(shí)現(xiàn)
>>> from booktest.serializers import BookInfoSerializer
>>> serializer = BookInfoSerializer()
>>> serializer
BookInfoSerializer():
id = IntegerField(label='ID', read_only=True)
btitle = CharField(label='名稱', max_length=20)
bpub_date = DateField(allow_null=True, label='發(fā)布日期', required=False)
bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=-2147483648, required=False)
bcomment = IntegerField(label='評(píng)論量', max_value=2147483647, min_value=-2147483648, required=False)
image = ImageField(allow_null=True, label='圖片', max_length=100, required=False)
2. 指定字段
1) 使用fields來明確字段,__all__表名包含所有字段,也可以寫明具體哪些字段,如
class BookInfoSerializer(serializers.ModelSerializer):
"""圖書數(shù)據(jù)序列化器"""
class Meta:
model = BookInfo
fields = ('id', 'btitle', 'bpub_date')
2) 使用exclude可以明確排除掉哪些字段
class BookInfoSerializer(serializers.ModelSerializer):
"""圖書數(shù)據(jù)序列化器"""
class Meta:
model = BookInfo
exclude = ('image',)
3) 默認(rèn)ModelSerializer使用主鍵作為關(guān)聯(lián)字段,但是我們可以使用depth來簡單的生成嵌套表示,depth應(yīng)該是整數(shù),表明嵌套的層級(jí)數(shù)量。如:
class HeroInfoSerializer2(serializers.ModelSerializer):
class Meta:
model = HeroInfo
fields = '__all__'
depth = 1
形成的序列化器如下:
HeroInfoSerializer():
id = IntegerField(label='ID', read_only=True)
hname = CharField(label='名稱', max_length=20)
hgender = ChoiceField(choices=((0, 'male'), (1, 'female')), label='性別', required=False, validators=[<django.core.valators.MinValueValidator object>, <django.core.validators.MaxValueValidator object>])
hcomment = CharField(allow_null=True, label='描述信息', max_length=200, required=False)
hbook = NestedSerializer(read_only=True):
id = IntegerField(label='ID', read_only=True)
btitle = CharField(label='名稱', max_length=20)
bpub_date = DateField(allow_null=True, label='發(fā)布日期', required=False)
bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=-2147483648, required=False)
bcomment = IntegerField(label='評(píng)論量', max_value=2147483647, min_value=-2147483648, required=False)
image = ImageField(allow_null=True, label='圖片', max_length=100, required=False)
4) 顯示指明字段,如:
class HeroInfoSerializer(serializers.ModelSerializer):
hbook = BookInfoSerializer()
class Meta:
model = HeroInfo
fields = ('id', 'hname', 'hgender', 'hcomment', 'hbook')
5) 指明只讀字段
可以通過read_only_fields指明只讀字段,即僅用于序列化輸出的字段
class BookInfoSerializer(serializers.ModelSerializer):
"""圖書數(shù)據(jù)序列化器"""
class Meta:
model = BookInfo
fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
read_only_fields = ('id', 'bread', 'bcomment')
3. 添加額外參數(shù)
我們可以使用extra_kwargs參數(shù)為ModelSerializer添加或修改原有的選項(xiàng)參數(shù)
class BookInfoSerializer(serializers.ModelSerializer):
"""圖書數(shù)據(jù)序列化器"""
class Meta:
model = BookInfo
fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
extra_kwargs = {
'bread': {'min_value': 0, 'required': True},
'bcomment': {'min_value': 0, 'required': True},
}
# BookInfoSerializer():
# id = IntegerField(label='ID', read_only=True)
# btitle = CharField(label='名稱', max_length=20)
# bpub_date = DateField(allow_null=True, label='發(fā)布日期', required=False)
# bread = IntegerField(label='閱讀量', max_value=2147483647, min_value=0, required=True)
# bcomment = IntegerField(label='評(píng)論量', max_value=2147483647, min_value=0, required=True)
4.視圖
Django REST framwork 提供的視圖的主要作用:
- 控制序列化器的執(zhí)行(檢驗(yàn)、保存、轉(zhuǎn)換數(shù)據(jù))
- 控制數(shù)據(jù)庫查詢的執(zhí)行
4.1Request 與 Response
1. Request
REST framework 傳入視圖的request對象不再是Django默認(rèn)的HttpRequest對象,而是REST framework提供的擴(kuò)展了HttpRequest類的Request類的對象。
REST framework 提供了Parser解析器,在接收到請求后會(huì)自動(dòng)根據(jù)Content-Type指明的請求數(shù)據(jù)類型(如JSON、表單等)將請求數(shù)據(jù)進(jìn)行parse解析,解析為類字典對象保存到Request對象中。
Request對象的數(shù)據(jù)是自動(dòng)根據(jù)前端發(fā)送數(shù)據(jù)的格式進(jìn)行解析之后的結(jié)果。
無論前端發(fā)送的哪種格式的數(shù)據(jù),我們都可以以統(tǒng)一的方式讀取數(shù)據(jù)。
常用屬性
1).data
request.data 返回解析之后的請求體數(shù)據(jù)。類似于Django中標(biāo)準(zhǔn)的request.POST和 request.FILES屬性,但提供如下特性:
- 包含了解析之后的文件和非文件數(shù)據(jù)
- 包含了對POST、PUT、PATCH請求方式解析后的數(shù)據(jù)
- 利用了REST framework的parsers解析器,不僅支持表單類型數(shù)據(jù),也支持JSON數(shù)據(jù)
2).query_params
request.query_params與Django標(biāo)準(zhǔn)的request.GET相同,只是更換了更正確的名稱而已。
2. Response
rest_framework.response.Response
REST framework提供了一個(gè)響應(yīng)類Response,使用該類構(gòu)造響應(yīng)對象時(shí),響應(yīng)的具體數(shù)據(jù)內(nèi)容會(huì)被轉(zhuǎn)換(render渲染)成符合前端需求的類型。
REST framework提供了Renderer 渲染器,用來根據(jù)請求頭中的Accept(接收數(shù)據(jù)類型聲明)來自動(dòng)轉(zhuǎn)換響應(yīng)數(shù)據(jù)到對應(yīng)格式。如果前端請求中未進(jìn)行Accept聲明,則會(huì)采用默認(rèn)方式處理響應(yīng)數(shù)據(jù),我們可以通過配置來修改默認(rèn)響應(yīng)格式。
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ( # 默認(rèn)響應(yīng)渲染類
'rest_framework.renderers.JSONRenderer', # json渲染器
'rest_framework.renderers.BrowsableAPIRenderer', # 瀏覽API渲染器
)
}
構(gòu)造方式
Response(data, status=None, template_name=None, headers=None, content_type=None)
data數(shù)據(jù)不要是render處理之后的數(shù)據(jù),只需傳遞python的內(nèi)建類型數(shù)據(jù)即可,REST framework會(huì)使用renderer渲染器處理data。
data不能是復(fù)雜結(jié)構(gòu)的數(shù)據(jù),如Django的模型類對象,對于這樣的數(shù)據(jù)我們可以使用Serializer序列化器序列化處理后(轉(zhuǎn)為了Python字典類型)再傳遞給data參數(shù)。
參數(shù)說明:
data: 為響應(yīng)準(zhǔn)備的序列化處理后的數(shù)據(jù);status: 狀態(tài)碼,默認(rèn)200;template_name: 模板名稱,如果使用HTMLRenderer時(shí)需指明;headers: 用于存放響應(yīng)頭信息的字典;content_type: 響應(yīng)數(shù)據(jù)的Content-Type,通常此參數(shù)無需傳遞,REST framework會(huì)根據(jù)前端所需類型數(shù)據(jù)來設(shè)置該參數(shù)。
常用屬性:
1).data
傳給response對象的序列化后,但尚未render處理的數(shù)據(jù)
2).status_code
狀態(tài)碼的數(shù)字
3).content
經(jīng)過render處理后的響應(yīng)數(shù)據(jù)
3. 狀態(tài)碼
為了方便設(shè)置狀態(tài)碼,REST framewrok在rest_framework.status模塊中提供了常用狀態(tài)碼常量。
1)信息告知 - 1xx
HTTP_100_CONTINUE
HTTP_101_SWITCHING_PROTOCOLS
2)成功 - 2xx
HTTP_200_OK
HTTP_201_CREATED
HTTP_202_ACCEPTED
HTTP_203_NON_AUTHORITATIVE_INFORMATION
HTTP_204_NO_CONTENT
HTTP_205_RESET_CONTENT
HTTP_206_PARTIAL_CONTENT
HTTP_207_MULTI_STATUS
3)重定向 - 3xx
HTTP_300_MULTIPLE_CHOICES
HTTP_301_MOVED_PERMANENTLY
HTTP_302_FOUND
HTTP_303_SEE_OTHER
HTTP_304_NOT_MODIFIED
HTTP_305_USE_PROXY
HTTP_306_RESERVED
HTTP_307_TEMPORARY_REDIRECT
4)客戶端錯(cuò)誤 - 4xx
HTTP_400_BAD_REQUEST
HTTP_401_UNAUTHORIZED
HTTP_402_PAYMENT_REQUIRED
HTTP_403_FORBIDDEN
HTTP_404_NOT_FOUND
HTTP_405_METHOD_NOT_ALLOWED
HTTP_406_NOT_ACCEPTABLE
HTTP_407_PROXY_AUTHENTICATION_REQUIRED
HTTP_408_REQUEST_TIMEOUT
HTTP_409_CONFLICT
HTTP_410_GONE
HTTP_411_LENGTH_REQUIRED
HTTP_412_PRECONDITION_FAILED
HTTP_413_REQUEST_ENTITY_TOO_LARGE
HTTP_414_REQUEST_URI_TOO_LONG
HTTP_415_UNSUPPORTED_MEDIA_TYPE
HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE
HTTP_417_EXPECTATION_FAILED
HTTP_422_UNPROCESSABLE_ENTITY
HTTP_423_LOCKED
HTTP_424_FAILED_DEPENDENCY
HTTP_428_PRECONDITION_REQUIRED
HTTP_429_TOO_MANY_REQUESTS
HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE
HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
5)服務(wù)器錯(cuò)誤 - 5xx
HTTP_500_INTERNAL_SERVER_ERROR
HTTP_501_NOT_IMPLEMENTED
HTTP_502_BAD_GATEWAY
HTTP_503_SERVICE_UNAVAILABLE
HTTP_504_GATEWAY_TIMEOUT
HTTP_505_HTTP_VERSION_NOT_SUPPORTED
HTTP_507_INSUFFICIENT_STORAGE
HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
4.2視圖概覽
REST framework 提供了眾多的通用視圖基類與擴(kuò)展類,以簡化視圖的編寫。
視圖的繼承關(guān)系:

視圖的方法與屬性:

4.3視圖說明
1. 兩個(gè)基類
1)APIView
rest_framework.views.APIView
APIView是REST framework提供的所有視圖的基類,繼承自Django的View父類。
APIView與View的不同之處在于:
- 傳入到視圖方法中的是REST framework的
Request對象,而不是Django的HttpRequeset對象; - 視圖方法可以返回REST framework的
Response對象,視圖會(huì)為響應(yīng)數(shù)據(jù)設(shè)置(render)符合前端要求的格式; - 任何
APIException異常都會(huì)被捕獲到,并且處理成合適的響應(yīng)信息; - 在進(jìn)行dispatch()分發(fā)前,會(huì)對請求進(jìn)行身份認(rèn)證、權(quán)限檢查、流量控制。
支持定義的屬性:
- authentication_classes 列表或元祖,身份認(rèn)證類
- permissoin_classes 列表或元祖,權(quán)限檢查類
- throttle_classes 列表或元祖,流量控制類
在APIView中仍以常規(guī)的類視圖定義方法來實(shí)現(xiàn)get() 、post() 或者其他請求方式的方法。
舉例:
from rest_framework.views import APIView
from rest_framework.response import Response
# url(r'^books/$', views.BookListView.as_view()),
class BookListView(APIView):
def get(self, request):
books = BookInfo.objects.all()
serializer = BookInfoSerializer(books, many=True)
return Response(serializer.data)
2)GenericAPIView
rest_framework.generics.GenericAPIView
繼承自APIVIew,主要增加了操作序列化器和數(shù)據(jù)庫查詢的方法,作用是為下面Mixin擴(kuò)展類的執(zhí)行提供方法支持。通常在使用時(shí),可搭配一個(gè)或多個(gè)Mixin擴(kuò)展類。
提供的關(guān)于序列化器使用的屬性與方法
-
屬性:
- serializer_class 指明視圖使用的序列化器
-
方法:
-
get_serializer_class(self)
返回序列化器類,默認(rèn)返回
serializer_class,可以重寫,例如:def get_serializer_class(self): if self.request.user.is_staff: return FullAccountSerializer return BasicAccountSerializer -
get_serializer(self, args, *kwargs)
返回序列化器對象,主要用來提供給Mixin擴(kuò)展類使用,如果我們在視圖中想要獲取序列化器對象,也可以直接調(diào)用此方法。
注意,該方法在提供序列化器對象的時(shí)候,會(huì)向序列化器對象的context屬性補(bǔ)充三個(gè)數(shù)據(jù):request、format、view,這三個(gè)數(shù)據(jù)對象可以在定義序列化器時(shí)使用。
-
request 當(dāng)前視圖的請求對象
-
view 當(dāng)前請求的類視圖對象
-
format 當(dāng)前請求期望返回的數(shù)據(jù)格式
-
-
提供的關(guān)于數(shù)據(jù)庫查詢的屬性與方法
-
屬性:
- queryset 指明使用的數(shù)據(jù)查詢集
-
方法:
-
get_queryset(self)
返回視圖使用的查詢集,主要用來提供給Mixin擴(kuò)展類使用,是列表視圖與詳情視圖獲取數(shù)據(jù)的基礎(chǔ),默認(rèn)返回
queryset屬性,可以重寫,例如:def get_queryset(self): user = self.request.user return user.accounts.all() -
get_object(self)
返回詳情視圖所需的模型類數(shù)據(jù)對象,主要用來提供給Mixin擴(kuò)展類使用。
在試圖中可以調(diào)用該方法獲取詳情信息的模型類對象。
若詳情訪問的模型類對象不存在,會(huì)返回404。
該方法會(huì)默認(rèn)使用APIView提供的check_object_permissions方法檢查當(dāng)前對象是否有權(quán)限被訪問。
舉例:
# url(r'^books/(?P<pk>\d+)/$', views.BookDetailView.as_view()), class BookDetailView(GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request, pk): book = self.get_object() # get_object()方法根據(jù)pk參數(shù)查找queryset中的數(shù)據(jù)對象 serializer = self.get_serializer(book) return Response(serializer.data)
-
其他可以設(shè)置的屬性
- pagination_class 指明分頁控制類
- filter_backends 指明過濾控制后端
2. 五個(gè)擴(kuò)展類
作用:
提供了幾種后端視圖(對數(shù)據(jù)資源進(jìn)行曾刪改查)處理流程的實(shí)現(xiàn),如果需要編寫的視圖屬于這五種,則視圖可以通過繼承相應(yīng)的擴(kuò)展類來復(fù)用代碼,減少自己編寫的代碼量。
這五個(gè)擴(kuò)展類需要搭配GenericAPIView父類,因?yàn)槲鍌€(gè)擴(kuò)展類的實(shí)現(xiàn)需要調(diào)用GenericAPIView提供的序列化器與數(shù)據(jù)庫查詢的方法。
1)ListModelMixin
列表視圖擴(kuò)展類,提供list(request, *args, **kwargs)方法快速實(shí)現(xiàn)列表視圖,返回200狀態(tài)碼。
該Mixin的list方法會(huì)對數(shù)據(jù)進(jìn)行過濾和分頁。
源代碼:
class ListModelMixin(object):
"""
List a queryset.
"""
def list(self, request, *args, **kwargs):
# 過濾
queryset = self.filter_queryset(self.get_queryset())
# 分頁
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
# 序列化
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
舉例:
from rest_framework.mixins import ListModelMixin
class BookListView(ListModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request):
return self.list(request)
2)CreateModelMixin
創(chuàng)建視圖擴(kuò)展類,提供create(request, *args, **kwargs)方法快速實(shí)現(xiàn)創(chuàng)建資源的視圖,成功返回201狀態(tài)碼。
如果序列化器對前端發(fā)送的數(shù)據(jù)驗(yàn)證失敗,返回400錯(cuò)誤。
源代碼:
class CreateModelMixin(object):
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
# 獲取序列化器
serializer = self.get_serializer(data=request.data)
# 驗(yàn)證
serializer.is_valid(raise_exception=True)
# 保存
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save()
def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
3) RetrieveModelMixin
詳情視圖擴(kuò)展類,提供retrieve(request, *args, **kwargs)方法,可以快速實(shí)現(xiàn)返回一個(gè)存在的數(shù)據(jù)對象。
如果存在,返回200, 否則返回404。
源代碼:
class RetrieveModelMixin(object):
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
# 獲取對象,會(huì)檢查對象的權(quán)限
instance = self.get_object()
# 序列化
serializer = self.get_serializer(instance)
return Response(serializer.data)
舉例:
class BookDetailView(RetrieveModelMixin, GenericAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
def get(self, request, pk):
return self.retrieve(request)
4)UpdateModelMixin
更新視圖擴(kuò)展類,提供update(request, *args, **kwargs)方法,可以快速實(shí)現(xiàn)更新一個(gè)存在的數(shù)據(jù)對象。
同時(shí)也提供partial_update(request, *args, **kwargs)方法,可以實(shí)現(xiàn)局部更新。
成功返回200,序列化器校驗(yàn)數(shù)據(jù)失敗時(shí),返回400錯(cuò)誤。
源代碼:
class UpdateModelMixin(object):
"""
Update a model instance.
"""
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
# If 'prefetch_related' has been applied to a queryset, we need to
# forcibly invalidate the prefetch cache on the instance.
instance._prefetched_objects_cache = {}
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save()
def partial_update(self, request, *args, **kwargs):
kwargs['partial'] = True
return self.update(request, *args, **kwargs)
5)DestroyModelMixin
刪除視圖擴(kuò)展類,提供destroy(request, *args, **kwargs)方法,可以快速實(shí)現(xiàn)刪除一個(gè)存在的數(shù)據(jù)對象。
成功返回204,不存在返回404。
源代碼:
class DestroyModelMixin(object):
"""
Destroy a model instance.
"""
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete()
3. 幾個(gè)可用子類視圖
1) CreateAPIView
提供 post 方法
繼承自: GenericAPIView、CreateModelMixin
2)ListAPIView
提供 get 方法
繼承自:GenericAPIView、ListModelMixin
3)RetrieveAPIView
提供 get 方法
繼承自: GenericAPIView、RetrieveModelMixin
4)DestoryAPIView
提供 delete 方法
繼承自:GenericAPIView、DestoryModelMixin
5)UpdateAPIView
提供 put 和 patch 方法
繼承自:GenericAPIView、UpdateModelMixin
6)ListCreateAPIView
提供 get、post方法
繼承自: GenericAPIView、ListModelMixin、CreateModelMixin
7)RetrieveUpdateAPIView
提供 get、put、patch方法
繼承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
8)RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
繼承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
4.4視圖集ViewSet
使用視圖集ViewSet,可以將一系列邏輯相關(guān)的動(dòng)作放到一個(gè)類中:
- list() 提供一組數(shù)據(jù)
- retrieve() 提供單個(gè)數(shù)據(jù)
- create() 創(chuàng)建數(shù)據(jù)
- update() 保存數(shù)據(jù)
- destory() 刪除數(shù)據(jù)
ViewSet視圖集類不再實(shí)現(xiàn)get()、post()等方法,而是實(shí)現(xiàn)動(dòng)作 action 如 list() 、create() 等。
視圖集只在使用as_view()方法的時(shí)候,才會(huì)將action動(dòng)作與具體請求方式對應(yīng)上。如:
class BookInfoViewSet(viewsets.ViewSet):
def list(self, request):
books = BookInfo.objects.all()
serializer = BookInfoSerializer(books, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
try:
books = BookInfo.objects.get(id=pk)
except BookInfo.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = BookInfoSerializer(books)
return Response(serializer.data)
在設(shè)置路由時(shí),我們可以如下操作
urlpatterns = [
url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}),
url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'})
]
1. 常用視圖集父類
1) ViewSet
繼承自APIView與ViewSetMixin,作用也與APIView基本類似,提供了身份認(rèn)證、權(quán)限校驗(yàn)、流量管理等。
ViewSet主要通過繼承ViewSetMixin來實(shí)現(xiàn)在調(diào)用as_view()時(shí)傳入字典(如{'get':'list'})的映射處理工作。
在ViewSet中,沒有提供任何動(dòng)作action方法,需要我們自己實(shí)現(xiàn)action方法。
2)GenericViewSet
使用ViewSet通常并不方便,因?yàn)閘ist、retrieve、create、update、destory等方法都需要自己編寫,而這些方法與前面講過的Mixin擴(kuò)展類提供的方法同名,所以我們可以通過繼承Mixin擴(kuò)展類來復(fù)用這些方法而無需自己編寫。但是Mixin擴(kuò)展類依賴與GenericAPIView,所以還需要繼承GenericAPIView。
GenericViewSet就幫助我們完成了這樣的繼承工作,繼承自GenericAPIView與ViewSetMixin,在實(shí)現(xiàn)了調(diào)用as_view()時(shí)傳入字典(如{'get':'list'})的映射處理工作的同時(shí),還提供了GenericAPIView提供的基礎(chǔ)方法,可以直接搭配Mixin擴(kuò)展類使用。
舉例:
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
url的定義
urlpatterns = [
url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})),
url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})),
]
3)ModelViewSet
繼承自GenericViewSet,同時(shí)包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
4)ReadOnlyModelViewSet
繼承自GenericViewSet,同時(shí)包括了ListModelMixin、RetrieveModelMixin。
2. 視圖集中定義附加action動(dòng)作
在視圖集中,除了上述默認(rèn)的方法動(dòng)作外,還可以添加自定義動(dòng)作。
添加自定義動(dòng)作需要使用rest_framework.decorators.action裝飾器。
以action裝飾器裝飾的方法名會(huì)作為action動(dòng)作名,與list、retrieve等同。
action裝飾器可以接收兩個(gè)參數(shù):
- methods: 該action支持的請求方式,列表傳遞
- detail: 表示是action中要處理的是否是視圖資源的對象(即是否通過url路徑獲取主鍵)
- True 表示使用通過URL獲取的主鍵對應(yīng)的數(shù)據(jù)對象
- False 表示不使用URL獲取主鍵
舉例:
from rest_framework import mixins
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
# detail為False 表示不需要處理具體的BookInfo對象
@action(methods=['get'], detail=False)
def latest(self, request):
"""
返回最新的圖書信息
"""
book = BookInfo.objects.latest('id')
serializer = self.get_serializer(book)
return Response(serializer.data)
# detail為True,表示要處理具體與pk主鍵對應(yīng)的BookInfo對象
@action(methods=['put'], detail=True)
def read(self, request, pk):
"""
修改圖書的閱讀量數(shù)據(jù)
"""
book = self.get_object()
book.bread = request.data.get('read')
book.save()
serializer = self.get_serializer(book)
return Response(serializer.data)
url的定義
urlpatterns = [
url(r'^books/$', views.BookInfoViewSet.as_view({'get': 'list'})),
url(r'^books/latest/$', views.BookInfoViewSet.as_view({'get': 'latest'})),
url(r'^books/(?P<pk>\d+)/$', views.BookInfoViewSet.as_view({'get': 'retrieve'})),
url(r'^books/(?P<pk>\d+)/read/$', views.BookInfoViewSet.as_view({'put': 'read'})),
]
3. action屬性
在視圖集中,我們可以通過action對象屬性來獲取當(dāng)前請求視圖集時(shí)的action動(dòng)作是哪個(gè)。
例如:
def get_serializer_class(self):
if self.action == 'create':
return OrderCommitSerializer
else:
return OrderDataSerializer
4. 視圖集的繼承關(guān)系


具體介紹可以查看 https://www.django-rest-framework.org/api-guide/fields/

浙公網(wǎng)安備 33010602011771號(hào)