關鍵詞:django by example
點我查看本文集的說明及目錄。
本項目相關內容包括:
實現(xiàn)過程:
CH12 創(chuàng)建一個API
上一章,我們創(chuàng)建了學生注冊和課程報讀系統(tǒng),創(chuàng)建視圖展示了課程內容,并學習了如何使用 Django 的緩存框架。本章,我們將學習如何實現(xiàn)以下功能:
- 創(chuàng)建 RESTful API
- 為 API 視圖處理授權和權限問題
- 創(chuàng)建 API 的 viewsets 和 routers
創(chuàng)建一個 RESTful API
我們可能需要創(chuàng)建一個接口來使其它服務與自己的 web 應用進行交互。通過 API 可以實現(xiàn)第三方獲得信息以及操作應用程序。
我們可以通過幾種方法構建 API ,但是推薦遵守 REST 原則。REST 架構源自 Representational State Transfer 。 RESTful APIs 是基于資源的。模型代表資源,GET、POST、PUT 或 DELETE 等 HTTP 方法可以獲取、創(chuàng)建、更新或者刪除對象。內容也可以使用 HTTP 響應代碼,不同的 HTTP 響應碼表示不同的 HTTP 請求結果,比如 2XX 響應碼表示成功,4XX 響應碼表示失敗等。
RESTful API 交換數(shù)據(jù)時最常使用的格式是 JSON 和 XML 。我們使用 JSON 序列化為項目創(chuàng)建一個 REST API 。 API 將提供以下功能:
- 獲取主題
- 獲取可以獲得的課程
- 獲取課程內容
- 報讀課程
我們可以通過創(chuàng)建自定義視圖開始學習使用 Django 構建 API 。然而,一些第三方模塊可以簡化項目創(chuàng)建 API 的過程,這些第三方模塊中最受歡迎的是 Django Rest 框架。
安裝 django Rest 框架
django Rest 框架幫助用戶輕松地創(chuàng)建項目的 REST API 。我們可以從http://www.django-rest-framework.org/找到 REST 框架的所有信息。
打開 shell 并使用以下命令安裝框架:
pip install djangorestframework
編輯 educa 項目的 settings.py 文件并在 INSTALLED_APPS 中添加 rest_framework 來激活應用:
INSTALLED_APPS = ['courses', 'django.contrib.admin', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.staticfiles',
'students', 'memcache_status','rest_framework',]
然后,在 settings.py 中添加以下設置:
# REST settings
REST_FRAMEWORK = {'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly']}
我們可以使用 REST_FRAMEWORK 配置 API 。 REST 框架提供很多配置默認行為的設置。 DEFAULT_PERMISSION_CLASSES 設置指定讀取、創(chuàng)建、更新或者刪除對象的默認權限。 我們將 DjangoModelPermissionsOrAnonReadOnly 設置為唯一的權限類。這個類基于 Django 權限系統(tǒng),權限系統(tǒng)允許用戶創(chuàng)建、更新和刪除對象,但匿名用戶只能讀取對象。后續(xù)我們將學習更多的權限。
REST 框架的參數(shù)設置列表見http://www.django-rest-framework.org/api-guide/settings/。
定義 serializers
設置好 REST 框架后,需要指定數(shù)據(jù)如何進行序列化。輸出數(shù)據(jù)應該序列化為特定格式,輸入數(shù)據(jù)應該進行反序列化以便于處理??蚣芴峁┮韵骂悂磉M行單一對象的序列化:
Serializer:為普通 Python 類實例提供序列化;
ModelSerializer:為模型實例提供序列化,使用主鍵表示對象關系;
HyperlinkedModelSerializer:與 ModelSerializer 相同,但是使用鏈接表示對象關系。
我們來創(chuàng)建第一個 Serializer 。在 courses 應用目錄下創(chuàng)建下面的文件結構:

我們在 api 目錄下創(chuàng)建所有的 API 函數(shù)。編輯 serializers.py 文件并添加以下代碼:
from rest_framework import serializers
from ..models import Subject
class SubjectSerializer(serializers.ModelSerializer):
class Meta:
model = Subject
fields = ('id', 'title', 'slug')
這是 Subject 模型的 serializer 。serializer 的定義方式與 Form 和 ModelForm 類的定義方式相似。使用 Meta 類來指定進行序列化的模型以及模型包含的字段。如果不設置 fields 屬性,則將包含模型所有字段。
我們來測試一下 serializer 。打開命令行并使用 python manage.py shell 打開 Django shell ,運行以下代碼:
In [1]: from courses.models import Subject
In [2]: from courses.api.serializers import SubjectSerializer
In [3]: subject = Subject.objects.latest('id')
In [4]: serializer = SubjectSerializer(subject)
In [5]: serializer.data
在這個例子中,我們獲取了一個 Subject 對象,創(chuàng)建了一個 SubjectSerializer 實例,并且訪問了序列化數(shù)據(jù)。將得到以下輸出:
{'id': 4, 'title': 'Mathematics', 'slug': 'mathematics'}
我們可以看到,模型數(shù)據(jù)變成了 Python 數(shù)據(jù)類型。
理解 pasers 和 renderers
HTTP 響應返回序列化數(shù)據(jù)之前,需要將序列化數(shù)據(jù)渲染為特定格式。接收到 HTTP 請求時,我們需要解析輸入數(shù)據(jù)并在使用之前對其進行反序列化。 REST 框架包含 renderers 和 parsers 來處理這個過程。
我們來看下如何解析輸入數(shù)據(jù)。對于一個 JSON 字符串輸入,可以使用 REST 框架提供的 JSONParser 類來將其轉換為 Python 對象。在 Python shell 中執(zhí)行以下代碼:
In [6]: from io import BytesIO
In [7]: from rest_framework.parsers import JSONParser
In [8]: data = b'{"id":4,"title":"Music","slug":"music"}'
In [9]: JSONParser().parse(BytesIO(data))
你應該得到這樣的輸出:
Out[9]: {u'id': 4, u'slug': u'music', u'title': u'Music'}
REST 框架還包括 Renderer 類來幫助用戶格式化 API 響應??蚣芨鶕?jù)內容確定使用哪個 renderer 。它檢查請求的 Accept 頭來確定預期的響應內容類型,比如可以由 URL 的格式后綴確定選用的 renderer ,例如,訪問將觸發(fā) JSONRenderer 來返回 JSON 響應。
回到 shell 并執(zhí)行以下代碼使用前面的序列化例子渲染 serializer 對象:
In [10]: from rest_framework.renderers import JSONRenderer
In [11]: JSONRenderer().render(serializer.data)
輸出應該是:
Out[11]: b'{"id":4,"title":"Mathematics","slug":"mathematics"}'
這里使用 JSONRenderer 將序列化數(shù)據(jù)渲染為 JSON 格式。默認情況下,REST 框架使用兩種不同的 renderers:JSONRenderer 和 BrowsableAPIRenderer 。BrowsableAPIRenderer 提供便于瀏覽 API 的 web 接口。我們可以通過設置 REST_FRAMEWORK 的 DEFAULT_RENDERCLASSES 來更改默認的 renderer 類。
http://www.django-rest-framework.org/api-guide/renderers/ 和http://www.django-rest-framework.org/api-guide/parsers/中有更多關于 renderers 和 parsers 的信息。
創(chuàng)建列表和詳情視圖
REST 框架內置創(chuàng)建 API 視圖的通用視圖和 mixin 集合來實現(xiàn)獲取、創(chuàng)建、更新或者刪除模型對象的功能。http://www.django-rest-framework.org/api-guide/generic-views/中包括 REST 框架提供的所有通用 mixin 和視圖。
我們來創(chuàng)建獲取 Subject 對象的列表和詳情視圖。在 courses/api/ 目錄下新建一個 views.py 的文件,并添加以下代碼:
from rest_framework import generics
from .serializers import SubjectSerializer
from ..models import Subject
class SubjectListView(generics.ListAPIView):
queryset = Subject.objects.all()
serializer_class = SubjectSerializer
class SubjectDetailView(generics.RetrieveAPIView):
queryset = Subject.objects.all()
serializer_class = SubjectSerializer
代碼中使用了 REST 框架的通用 ListAPIView 和 RetrieveAPIView。詳細視圖中包含一個獲取給定鍵對象的 pk URL參數(shù)。兩個視圖都設置了以下屬性:
- queryset: 獲取對象使用的基礎 QuerySet ;
- serializer_class:序列化對象的類;
下面為視圖添加 URL模式。在 courses/api/ 目錄下新建 urls.py 的文件并添加以下代碼:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^subjects/$', views.SubjectListView.as_view(), name='subject_list'),
url(r'^subjects/(?P<pk>\d+)/$', views.SubjectDetailView.as_view(),
name='subject_detail'), ]
編輯 educa 項目的 urls.py 文件并包含以下 API 模式:
url(r'^api/',include('courses.api.urls'))
使用 python manage.py runserver 運行開發(fā)服務器,打開 shell 并通過 cURL 獲取 http://127.0.0.1:8000/api/subjects/:
curl http://127.0.0.1:8000/api/subjects/
你將得到類似下面的輸出:
[{"id":4,"title":"Mathematics","slug":"mathematics"},{"id":3,"title":"Music","slug":"music"},{"id":2,"title":"Physics","slug":"physics"},{"id":1,"title":"Programming","slug":"programming"}]
HTTP響應包含 JSON 格式的 Subject 對象列表。如果你的操作系統(tǒng)沒有安裝 curl,可以從http://curl.haxx.se/dlwiz/下載。除了 curl ,我們還可以使用其它工具發(fā)送 HTTP請求,比如 Postman 瀏覽器插件(可以從 https://www.getpostman.com 獲?。?。
在瀏覽器中打開 http://127.0.0.1:8000/api/subjects/ ,你將看到 REST 框架可以瀏覽的 API :

這個 HTML接口由 BrowsableAPIRenderer 提供。它展示了得到的標題和內容,并且允許用戶實現(xiàn)請求。我們可以通過在 URL 中添加 id 來訪問 Subject 對象的 API 詳情視圖。在瀏覽器中打開http://127.0.0.1:8000/api/subjects/1/,你將看到渲染為 JSON 格式的單個對象。
創(chuàng)建嵌套 serializers
我們將為 Course 模型創(chuàng)建 serializer ,編輯 api/serializers.py 文件并添加以下代碼:
from ..models import Course
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = (
'id', 'subject', 'title', 'slug', 'overview', 'created', 'owner',
'modules')
我們來看下如何實現(xiàn) Course 對象序列化,打開 shell ,運行 python manage.py shell ,并運行以下代碼:
In [1]: from rest_framework.renderers import JSONRenderer
In [2]: from courses.models import Course
In [3]: from courses.api.serializers import CourseSerializer
In [4]: course = Course.objects.latest('id')
In [5]: serializer = CourseSerializer(course)
In [6]: JSONRenderer().render(serializer.data)
我們將會看到包含 CourserSerializer 設置的字段的 JSON 對象。
b'{"id":2,"subject":4,"title":"Course 2","slug":"course2","overview":"","created":"2018-04-16T15:43:35.885525Z","owner":1,"modules":[5,6]}'
結果中的 modules 管理器的相關對象序列化為主鍵列表:
"modules":[7,8]
我們希望增加更多模信息,因此需要序列化 Module 對象并進行嵌套。將剛剛添加到 api/serializers.py 中的代碼修改為:
from ..models import Course, Module
class ModuleSerializer(serializers.ModelSerializer):
class Meta:
model = Module
fields = ('order', 'title', 'description')
class CourseSerializer(serializers.ModelSerializer):
modules = ModuleSerializer(many=True, read_only=True)
class Meta:
model = Course
fields = (
'id', 'subject', 'title', 'slug', 'overview', 'created', 'owner',
'modules')
這里定義了 ModuleSerializer 來對 Module 模型進行序列化。然后將 modules 屬性添加到 CourseSerializer 中來嵌套 ModuleSerializer 。設置 many = True 表示對多個對象進行序列化。read_only 參數(shù)表示這個字段是只讀的,不能使用任何輸入來創(chuàng)建或者修改對象。
打開 shell 并再次創(chuàng)建 CourseSerializer 實例,使用 JSONRenderer 渲染序列化數(shù)據(jù)。這次,模塊列表使用了嵌套的 ModuleSerializer 進行了序列化:
"modules":[{"order":0,"title":"Installing Django","description":"how to install django"},{"order":1,"title":"models","description":"about django model"}]
更多序列化的相關信息見http://www.django-rest-framework.org/api-guide/serializers/。
創(chuàng)建自定義視圖
REST 框架提供一個 APIView 類,可以基于 Django View 類實現(xiàn) API 功能。 APIView 與 View 的區(qū)別在于使用 REST 框架 自定義的 Request 和 Response 對象并處理 APIException 異常來返回合適的 HTTP 響應。它還包含內置的授權和權限系統(tǒng)來管理視圖訪問。
我們將為用戶報讀課程創(chuàng)建一個視圖,編輯 api/views.py 文件并添加以下代碼:
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView
from rest_framework.response import Response
from ..models import Course
class CourseEnrollView(APIView):
def post(self, request, pk, format=None):
course = get_object_or_404(Course, pk=pk)
course.students.add(request.user)
return Response({'enrolled': True})
CourseEnrollView 視圖處理課程的用戶報讀事務,上面的代碼實現(xiàn)以下功能:
- 創(chuàng)建自定義的 APIView 子類;
- 為 POST 請求定義 post() 方法,這個視圖不允許其它方法;
- 使用 pk URL 參數(shù)來表示課程 ID 。獲取給定 pk 參數(shù)的課程,如果沒有對象課程則引發(fā) 404 異常;
- 將當前的用戶添加到 Course 對象的 students 多對多關系中并返回成功響應。
編輯 api/urls.py 文件并為 CourseEnrollView 添加以下 URL 模式:
url(r'^courses/(?P<pk>\d+)/enroll/$', views.CourseEnrollView.as_view(),
name='course_enroll')
理論上,現(xiàn)在已經(jīng)可以通過 POST 請求來報讀課程,然而,我們還需要識別用戶并阻止沒有授權的用戶訪問視圖。下面來看下 API 的授權和權限如何工作。
處理授權
REST 框架通過授權類來識別請求用戶。如果授權成功,框架將 request.user 設置為授權的 User 對象,如果用戶沒有授權,Request.user 將設置為 Django AnonymousUser 實例。
REST 框架提供以下授權后端:
BasicAuthentication : HTTP 基本授權,客戶端在 Authorization HTTP頭中發(fā)送經(jīng)過 Base64 編碼處理的用戶名和密碼。https://en.wikipedia.org/wiki/Basic_access_authentication中有詳細介紹。
TokenAuthertication: 令牌授權。使用 Token 模型來保存用戶令牌。通過放在 Authorization HTTP 頭中的 token 進行授權。
SessionAuthertication:使用 Django 會話后端進行授權。這個后端用于從網(wǎng)站前端向 API 實現(xiàn)授權的 AJAX 請求。
我們可以通過繼承 REST 框架提供的 BaseAuthorization 類創(chuàng)建一個自定義授權后端,并覆蓋 authenticate() 方法。
每個視圖都可以進行授權,也可以使用 DEFAULT_AUTHENTICATION_CLASSES 設置全局授權。
注意:
授權僅能識別請求用戶。不能允許或者拒絕訪問視圖。我們需要使用權限來限制對視圖的訪問。
我們可以從http://www.django-rest-framework.org/api-guide/authentication/ 找到授權的所有信息。
下面將在視圖中添加 BasicAuthentication 。編輯 courses 應用的 api/views.py 并向 CourseEnrollView 添加 authentication_class 屬性:
from rest_framework.authentication import BasicAuthentication
class CourseEnrollView(APIView):
authentication_classes = (BasicAuthentication,)
def post(self, request, pk, format=None):
course = get_object_or_404(Course, pk=pk)
course.students.add(request.user)
return Response({'enrolled': True})
這里將通過 HTTP 請求頭中的 Authorization 標頭憑證來識別用戶。
為視圖添加權限
REST 框架使用權限系統(tǒng)來限制視圖訪問。REST 框架內置的權限包括:
- AllowAny : 沒有訪問限制,用戶授權與否都可以訪問;
- IsAuthenticated : 只允許授權的用戶訪問;
- IsAuthenticatedOrReadOnly : 授權用戶可以進行完全訪問,匿名用戶只能進行讀操作(如 GET 、 HEAD、或 OPTIONS 操作);
- DjangoModelPermissions : django.contrib.auth 綁定的權限,視圖需要設置 queryset 屬性。 只有有模型訪問權限的用戶才能訪問。
- DjangoObjectPermissions : 基于對象的 Django 權限。
如果拒絕用戶訪問,我們通常獲得以下 HTTP 錯誤碼:
- HTTP 401 :沒授權
- HTTP 403:權限拒絕
我們可以從http://www.django-rest-framework.org/api-guide/permissions/看到更多關于權限的介紹。
編輯 courses 應用的 api/views.py 文件并為 CourseEnrollView 添加 permission_class 屬性:
from rest_framework.permissions import IsAuthenticated
class CourseEnrollView(APIView):
authentication_classes = (BasicAuthentication,)
permission_classes = (IsAuthenticated,)
這里添加了 IsAuthorization 權限,將阻止匿名用戶訪問視圖,現(xiàn)在可以向 API 發(fā)送 POST 請求了。
保證開發(fā)服務器正在運行,打開 shell 并運行以下命令:
curl -i -X POST http://127.0.0.1:8000/api/courses/1/enroll/
你將得到這樣的響應:
HTTP/1.0 401 Unauthorized
Date: Mon, 05 Mar 2018 07:00:42 GMT
Server: WSGIServer/0.1 Python/2.7.10
Content-Length: 58
Vary: Accept
Allow: POST, OPTIONS
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
WWW-Authenticate: Basic realm="api"
{"detail":"Authentication credentials were not provided."}
這里接收到了預期的 401 HTTP 碼,這是由于我們沒有授權。使用一個有基本權限的用戶,運行以下命令:
curl -i -X POST -u student:password http://127.0.0.1:8000/api/courses/1/enroll/
使用注冊過的用戶的憑證代替 student:password ,將會看到下面的輸出:
HTTP/1.0 200 OK
Date: Mon, 05 Mar 2018 07:07:50 GMT
Server: WSGIServer/0.1 Python/2.7.10
Vary: Accept
X-Frame-Options: SAMEORIGIN
Content-Type: application/json
Content-Length: 17
Allow: POST, OPTIONS
{"enrolled":true}
我們可以訪問 admin 網(wǎng)站并檢查用戶是否報讀了課程。
創(chuàng)建視圖集合和routers
ViewSets 幫助我們定義 API 與 REST 框架(通過 Router 對象)動態(tài)創(chuàng)建的 URLs 的交互。使用視圖集合,可以避免多個視圖使用重復邏輯。視圖集合包含 list()、create()、retrieve()、update()、partial_update() 和 destroy() 等實現(xiàn)獲取、更新、刪除等操作動作的方法。
下面為 Course 模型創(chuàng)建一個視圖集合,編輯 api/views.py 并添加以下代碼:
from rest_framework import viewsets
from .serializers import CourseSerializer
class CourseViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Course.objects.all()
serializer_class = CourseSerializer
創(chuàng)建 ReadOnlyModelViewSet 子類,ReadOnlyModelViewSet 提供只讀權限的 list() 和 retrieve() 操作來列出對象集合或獲取單個對象。編輯 api/urls.py 并為視圖集合創(chuàng)建 router :
from django.conf.urls import url, include
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register('courses', views.CourseViewSet)
urlpatterns = [
url(r'^subjects/$', views.SubjectListView.as_view(), name='subject_list'),
url(r'^subjects/(?P<pk>\d+)/$', views.SubjectDetailView.as_view(),
name='subject_detail'),
url(r'^courses/(?P<pk>\d+)/enroll/$', views.CourseEnrollView.as_view(),
name='course_enroll'),
url(r'^', include(router.urls)), ]
這里創(chuàng)建了 DefaultRouter 對象并使用 courses 前綴注冊視圖集合,router 負責為視圖集合動態(tài)生成 URLs 。
在瀏覽器中打開 http://127.0.0.1:8000/api/,將會看到 router 在 URL 中列出所有視圖集合,如下圖所示:

可以訪問 http://127.0.0.1:8000/api/courses/ 來獲得課程列表。
可以從 http://www.django-rest-framework.org/api-guide/viewsets/了解更多關于視圖集合的消息??梢詮?a target="_blank" rel="nofollow">http://www.django-rest-framework.org/api-guide/routers/找到更多關于 router 的消息。
為視圖集合添加額外動作
Viewsets 還可以添加額外動作。我們來將 CourseEnrollView 視圖更改為自定義視圖集合動作。編輯
api/views.py 文件并更改 CourseViewSet :
from rest_framework.decorators import action
class CourseViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Course.objects.all()
serializer_class = CourseSerializer
@action(methods=['post'], detail=True,
authentication_classes=[BasicAuthentication],
permission_classes=[IsAuthenticated])
def enroll(self, request, *args, **kwargs):
course = self.get_object()
course.students.add(request.user)
return Response({'enrolled': True})
筆者注:
原文代碼為:
from rest_framework.decorators import detail_route class CourseViewSet(viewsets.ReadOnlyModelViewSet): queryset = Course.objects.all() serializer_class = CourseSerializer @detail_route(methods=['post'], authentication_classes=[BasicAuthentication], permission_classes=[IsAuthenticated]) def enroll(self, request, *args, **kwargs): course = self.get_object() course.students.add(request.user) return Response({'enrolled': True})由于 rest_framework 3.10 版本之后將棄用 detail_route,這里使用 action( detail=True )代替了原文中的 detail_route。
這里添加了自定義 enroll() 方法實現(xiàn)視圖集合的額外動作,上面的代碼功能為:
使用框架的 action 裝飾器指定這是對單個對象進行操作的動作;
可以使用裝飾器為動作添加自定義屬性,這里指定只有 POST 方法可以訪問視圖,并且設置了授權類和權限類;
使用 self.get_object() 來獲得 Course 對象;
將當前用戶添加到 students 多對多關系中并返回自定義成功響應。
編輯 api/urls.py 文件并刪除下面的 URL ,因為不再需要這個鏈接:
url(r'^courses/(?P<pk>\d+)/enroll/$', views.CourseEnrollView.as_view(),
name='course_enroll'),
然后編輯 api/views.py 文件并刪除 CourseEnrollView 類。
現(xiàn)在,報讀課程的鏈接可以通過 router 動態(tài)生成,由于使用名為 enroll 的動作自動生成,生成的 URL 與之前的一樣。
創(chuàng)建自定義權限
我們期望學生能夠訪問報讀了的課程內容。只有報讀了課程的學生才能訪問內容。實現(xiàn)這個功能的最好方法是自定義權限類。rest_framework 提供 BasePermission 類來幫助用戶定義以下方法:
- has_permission() :視圖級別的權限檢查;
- has_object_permission() :實例級別的權限檢查;
這些方法返回 True 表示成功,返回 False 表示失敗。在 courses/api/ 目錄下新建 permissions.py 的文件,并添加以下代碼:
from rest_framework.permissions import BasePermission
class IsEnrolled(BasePermission):
def has_object_permission(self, request, view, obj):
return obj.students.filter(id=request.user.id).exists()
創(chuàng)建 BasePermission 的子類并重寫 has_object_permission() 方法。檢查請求的用戶是否在 Course 對象的 students 關系中,下一步我們將使用 IsEnrolled 權限。
序列化課程內容
我們需要序列化課程內容, Content 模型內置通用外鍵來訪問不同類型內容模型。前一章,我們還為所有方法模型添加了自定義 render() 方法。這個方法可以為 API 提供渲染的內容。
編輯 courses 應用的 api/serializers.py 文件并添加以下代碼:
from ..models import Content
class ItemRelatedField(serializers.RelatedField):
def to_representation(self, value):
return value.render()
class ContentSerializer(serializers.ModelSerializer):
item = ItemRelatedField(read_only=True)
class Meta:
model = Content
fields = ('order', 'item')
上面的代碼,繼承 REST 框架提供的 RelatedField 序列化字段創(chuàng)建自定義字段,并重寫 to_representation() 方法。為 Content 模型定義 ContentSerializer 并使用自定義字段表示通用外鍵 item 。
Module 模型還需要創(chuàng)建序列化器來包含課程內容并擴展的 Course 序列化器。編輯 api/serializers.py 文件并添加以下代碼:
class ModuleWithContentsSerializer(serializers.ModelSerializer):
contents = ContentSerializer(many=True)
class Meta:
model = Module
fields = ('order', 'title', 'description', 'contents')
class CourseWithContentsSerializer(serializers.ModelSerializer):
modules = ModuleWithContentsSerializer(many=True)
class Meta:
model = Course
fields = (
'id', 'subject', 'title', 'slug', 'overview', 'created', 'owner',
'modules')
我們來創(chuàng)建一個模仿 retrieve() 課程內容動作的視圖。編輯 api/views.py 文件并在 CourseViewSet 類中添加以下方法:
from .permissions import IsEnrolled
from .serializers import CourseWithContentsSerializer
class CourseViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Course.objects.all()
serializer_class = CourseSerializer
@action(methods=['post'], detail=True,
authentication_classes=[BasicAuthentication],
permission_classes=[IsAuthenticated])
def enroll(self, request, *args, **kwargs):
course = self.get_object()
course.students.add(request.user)
return Response({'enrolled': True})
@action(methods=['get'], serializer_class=CourseWithContentsSerializer,
authentication_classes=[BasicAuthentication],
permission_classes=[IsAuthenticated])
def contents(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
這個方法的描述如下:
使用 detail_route 裝飾器指定這個動作應用于單個對象;
指定只有 GET 方法可以訪問這個動作;
使用 CourseWithContentsSerializer 序列化類包含渲染的課程內容;
使用 IsAutheticated 和自定義 IsEnrolled 權限指定動作權限,這樣可以保證只有訂閱了課程的用戶才能訪問內容。
使用存在的 retrieve() 動作返回課程對象。
在瀏覽器中打開 http://127.0.0.1:8000/api/courses/1/contents/,如果使用具有權限的用戶訪問視圖,將可以看到課程的每個模塊,模塊包含渲染的 HTML 內容:

我們已經(jīng)創(chuàng)建了一個幫助其它服務程序訪問課程應用的簡單 API 。REST 框架可以使用 ModelViewSet 視圖集來管理創(chuàng)建和編輯對象。我們已經(jīng)介紹了 Django Rest 框架的主要內容,更多擴展內容詳見http://www.django-rest-framework.org/。
總結
本章,我們創(chuàng)建了一個 RESTful API 供其它服務與 web 應用交互。
第 13章 上線運行 可以從https://www.packtpub.com/sites/default/files/downloads/Django_By_Example_GoingLive.pdf下載。它將教我們如何通過 uWSGI 和 NGINX 創(chuàng)建生產(chǎn)環(huán)境,以及如何實現(xiàn)自定義 middleware 和創(chuàng)建自定義管理命令。
你已經(jīng)到達了本書的結尾。恭喜你!已經(jīng)學習了使用 Django 成功地創(chuàng)建 web 應用的技能。這本書引導你開發(fā)實際生活中需要的項目以及使用 Django 集成其它技術。現(xiàn)在,你已經(jīng)為創(chuàng)建自己的 Django 項目做好了準備,不管它是簡單的原型還是大型的 web應用程序。
祝下一次的 Django 冒險成功。