做者:HelloGitHub-追夢人物html
此前在討論基於模板引擎的開發方式和 django-rest-framework 開發的異同時說過,django-rest-framework 開發和傳統的開發方式沒有什麼不一樣,區別僅在於返回的數據格式不一樣而已。前端
在基於模板引擎的開發方式中,博客首頁文章列表的視圖函數多是這樣的:web
from django.shortcuts import render
from .models import Post
def index(request):
post_list = Post.objects.all().order_by('-created_time')
return render(request, 'blog/index.html', context={'post_list': post_list})
複製代碼
在 django-rest-framework,代碼邏輯是同樣的,只是在最後返回結果時,返回資源序列化後的結果。數據庫
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from .models import Post
from .serializers import PostListSerializer
@api_view(http_method_names=["GET"])
def index(request):
post_list = Post.objects.all().order_by('-created_time')
serializer = PostListSerializer(post_list, many=True)
return Response(serializer.data, status=status.200)
複製代碼
暫且忽略掉資源序列化器 PostListSerializer
,咱們接下來會實現它,先把注意力放在主體邏輯上。django
首先,咱們從 rest_framework.decorators
中導入了 api_view 裝飾器,並用它裝飾了 index 視圖函數,使其成爲一個 RESTful API 視圖函數。api
爲何須要這個視圖函數裝飾器呢?以前說過,django-rest-framework 爲 API 的開發提供了豐富的功能,包括內容協商、認證和鑑權、限流等等。這些過程 django 默認的視圖函數在處理 HTTP 請求時是沒有提供的,而通過 api_view
裝飾後的視圖,則提供了上述所有功能。瀏覽器
不過咱們這裏並無看到任何內容協商、認證和鑑權、限流代碼邏輯和配置,這是爲何呢?緣由隱藏在 Python 的裝飾器魔法裏,django-rest-framework 對於上述功能有一套默認的處理邏輯,所以咱們不須要進行任何配置,僅需使用 api_view
裝飾一個 django 視圖函數,全部功能所有自動開啓。前端框架
視圖函數裏咱們先從數據庫獲取文章列表資源,而後使用序列化器對其進行序列化,序列化後的數據存在 data
屬性裏,咱們把它傳遞給 HTTP 響應類 Response
,並將這個響應返回。服務器
注意這個 Response
是從 rest_framework.response
中導入的,它相似於 django 的 HTTPResponse 響應類。實際上,這個類是 django-rest-framework 對 django 的模板響應類(SimpleTemplateResponse)的拓展(具體的細節能夠不用瞭解,只要知道 django 使用它來渲染模板並構造 HTTP 響應便可),一般在 RESTful API 的視圖函數中咱們都會返回這個類,而不是 django 的 HTTP 響應類。此外,經過傳入 status 參數,指定 HTTP 響應的狀態碼。架構
小貼士
請了解經常使用的 HTTP 狀態碼。在 RESTful 架構中,客戶端經過 HTTP 請求動詞表徵對資源的操做意圖,而服務端則使用 HTTP 狀態碼錶示資源操做的結果。經常使用狀態碼及其含義以下:
200:一般表示請求成功。
201:表示資源建立成功。
400:表示客戶端請求錯誤。
401:沒有提供身份認證信息
403:沒有操做權限
404 :訪問的資源不存在
405:不支持的 HTTP 請求方法
500:服務器內部錯誤
HTTP 請求和響應過程,django-rest-framework 已經幫咱們處理。可是資源的序列化,框架是沒法自動化完成的,框架提供了基本的序列化器,咱們須要自定義序列化邏輯。因此,讓咱們來定義 PostListSerializer
序列化器,用它來序列化文章列表。
序列化器由一系列的序列化字段(Field)組成,序列化字段的做用是,在序列化資源時,將 Python 數據類型轉爲原始數據類型(一般爲字符類型或者二進制類型),以便在客戶端和服務端之間傳遞;反序列化時,將原始數據類型轉爲 Python 數據類型。在轉換過程當中,還會進行數據合法性的校驗。
先來看一個簡單的例子(摘自 django-rest-framework 官網示例),理解序列化器的工做原理和功能。假設咱們有一個 Python 類 Comment
:
from datetime import datetime
class Comment(object):
def __init__(self, email, content, created=None):
self.email = email
self.content = content
self.created = created or datetime.now()
comment = Comment(email='leila@example.com', content='foo bar')
複製代碼
根據 Comment
類 3 個屬性的類型,定義一個序列化器,用於數據序列化和反序列化。咱們在上一步教程的 交流的橋樑:評論功能 中介紹過表單(Form)的定義。實際上,django-rest-framework 序列化器的設計參考了 django 表單的設計。序列化器和表單也有不少類似功能,好比對輸入數據進行校驗等。序列化器的代碼以下:
from rest_framework import serializers
class CommentSerializer(serializers.Serializer):
email = serializers.EmailField()
content = serializers.CharField(max_length=200)
created = serializers.DateTimeField()
複製代碼
自定義的序列化器都要繼承 serializers.Serializer
基類,基類提供了數據序列化和反序列化的邏輯。根據被序列化對象的屬性的數據類型,須要指定相應的序列化字段(Serializer Field)。django-rest-framework 提供了不少經常使用的序列化字段,例如本例中用於序列化 email 數據格式的 EmailField
,用於序列化字符型數據格式的 CharField
,用於序列化日期格式的 DateTimeField
。在實際項目中,應該根據數據類型,選擇合適的序列化字段。所有序列化字段,能夠參考官方文檔 Serializer fields。
有了序列化器,就能夠將 Comment
對象序列化了,序列化器用法以下:
>>> serializer = CommentSerializer(comment)
>>> serializer.data
# 輸出:
{'email': 'leila@example.com', 'content': 'foo bar', 'created': '2016-01-27T15:17:10.375877'}
複製代碼
首先將須要序列化的對象(comment)傳入序列化器(CommentSerializer),構造一個序列化器對象(serializer),訪問序列化器對象的 data 屬性,就能夠獲得序列化後的數據。
被序列化對象序列化後的數據是一個扁平的 Python 字典,字典中的數據描述了這個對象資源。有了序列化生成的 Python 字典,咱們就能夠將字典數據進一步格式化爲 JSON 字符串或者 XML 文檔字符串,在客戶端和服務端之間傳輸。試想,客戶端服務端一般都經過 HTTP 協議傳輸數據,傳輸的數據只能是字符串或者二進制數據,不可能將一個 Python 的對象直接傳遞,這就是爲何要序列化的緣由。一端接收到序列化的數據後,若是有須要,能夠對數據進行反序列化,從新恢復爲 Python 對象。
以上就是一個標準序列化器的定義。其關鍵點在於,根據被序列化對象屬性的數據類型,選擇合適的序列化字段。回顧咱們在上一步教程的 交流的橋樑:評論功能 中對評論表單的定義,咱們經過繼承 ModelForm
定義了表單,而並無顯示地指定表單字段的類型。緣由在於,對於 django 中的模型(Model),已經有了定義其數據類型的模型字段,所以 django 表單能夠根據關聯的模型,自動推測須要使用的表單字段,在背後幫咱們完成表單字段的選擇,簡化了表單的定義。
和表單相似,django-rest-framework 的序列化器也能夠根據關聯的模型,自動檢測被序列化模型各個屬性的數據類型,推測須要使用的序列化字段,無需咱們顯示定義。此時,自定義的序列化器再也不繼承標準的 Serializer
,而是繼承其子類,ModelSerializer
。
咱們來編寫文章(Post)模型的序列化器代碼。按照習慣,序列化器的代碼位於相應應用的 serializers.py 模塊中,所以在 blog 應用下新建一個 serializers.py 文件,寫上以下代碼:
from rest_framework import serializers
from .models import Post
class PostListSerializer(serializers.ModelSerializer):
category = CategorySerializer()
author = UserSerializer()
class Meta:
model = Post
fields = [
'id',
'title',
'created_time',
'excerpt',
'category',
'author',
'views',
]
複製代碼
使用 ModelSerializer
時,只須要在序列化器的內部類 Meta
中指定關聯的模型,以及須要序列化的模型屬性,django-rest-framework 就會根據各個屬性的數據類型,自動推測須要使用的系列化字段,從而生成標準的序列化器。事實上,咱們能夠來看一下 django-rest-framework 最終生成的序列化器長什麼樣子:
class PostListSerializer():
id = IntegerField(label='ID', read_only=True)
title = CharField(label='標題', max_length=70)
created_time = DateTimeField(label='建立時間', required=False)
excerpt = CharField(allow_blank=True, label='摘要', max_length=200, required=False)
category = CategorySerializer()
author = UserSerializer()
複製代碼
還須要注意一點,title
、created_time
、views
這些屬性都是原始的數據類型(字符型、日期型、整數類型)。而對於文章關聯的 category
、author
,它們自己也是一個對象,django-rest-framework 就沒法推測該使用什麼類型的系列化字段來序列化它們了。因此這裏咱們按照標準序列化器的定義方式,將這兩個屬性的系列化字段分別定義爲 CategorySerializer
、UserSerializer
,意思是告訴 django-rest-framework,請使用 CategorySerializer
和 UserSerializer
來序列化關聯的 category
和 author
。實際上,序列化器自己也是一個序列化字段。固然,CategorySerializer
和 UserSerializer
目前還不存在,咱們來定義他們:
from django.contrib.auth.models import User
from .models import Category, Post
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = [
'id',
'name',
]
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'id',
'username',
]
class PostListSerializer(serializers.ModelSerializer):
# ...
複製代碼
再來回顧一下咱們的 API 視圖函數代碼:
@api_view(http_method_names=["GET"])
def index(request):
post_list = Post.objects.all().order_by('-created_time')
serializer = PostListSerializer(post_list, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
複製代碼
注意這裏 PostListSerializer
的用法,構造序列化器時能夠傳入單個對象,序列化器會將其序列化爲一個字典;也能夠傳入包含多個對象的可迭代類型(這裏的 post_list 是一個 django 的 QuerySet),此時須要設置 many
參數爲 True
序列化器會依次序列化每一項,返回一個列表。
給 api_view
裝飾器傳入 http_method_names
參數指定容許訪問該 API 視圖的 HTTP 方法。
如今咱們已經有了視圖函數,最後,咱們須要給這個視圖函數綁定 URL,在 blog 應用下的 urls.py 中加入綁定的代碼:
path('api/index/', views.index)
複製代碼
啓動開發服務器,打開瀏覽器訪問 http://127.0.0.1:8000/api/index/ ,能夠看到接口返回了文章列表 JSON 格式的數據(默認爲 JSON)。
目前來講,這個接口其實做用不大。不過在後續的教程中,咱們學習前端框架 Vue,那個時候,RESTful API 就有了它的用武之地了。
回顧一下 index API 視圖函數的基本邏輯:
這實際上是訪問序列型的資源比較常見的邏輯,咱們知道,django 專門爲這種在 Web 開發中經常使用的邏輯提供了一系列基於類的通用視圖,以提升代碼的複用性和減小代碼量。只是 django 的通用視圖適用於基於模板引擎的開發方式,一樣的,django-rest-framework 也提供了專門針對 RESTful API 開發過程當中經常使用邏輯的類視圖通用函數。接下來,讓咱們使用 django-rest-framework 提供的通用類視圖,將首頁 API 的視圖函數改成類視圖。