第十二章 構(gòu)建API

12 構(gòu)建API

在上一章中柬帕,你構(gòu)建了一個學生注冊和課程報名系統(tǒng)。你創(chuàng)建了顯示課程內(nèi)容的視圖,并學習了如何使用Django的緩存框架隔节。在本章中鹅经,你會學習以下知識點:

  • 構(gòu)建一個RESTful API
  • 為API視圖處理認證和權(quán)限
  • 創(chuàng)建API視圖集和路由

12.1 構(gòu)建RESTful API

你可能想要創(chuàng)建一個接口,讓其它服務可以與你的web應用交互怎诫。通過構(gòu)建一個API瘾晃,你可以允許第三方以編程方式使用信息和操作你的應用。

你可以通過很多方式構(gòu)建API幻妓,但最好是遵循REST原則蹦误。REST架構(gòu)是表述性狀態(tài)傳遞(Representational State Transfer)的縮寫。RESTful API是基于資源的肉津。你的模型代表資源强胰,HTTP方法(比如GET,POST妹沙,PUT或DELETE)用于檢索偶洋,創(chuàng)建,更新或者刪除對象距糖。HTTP響應代碼也可以在這個上下文中使用玄窝。返回的不同HTTP響應代碼表示HTTP請求的結(jié)果,比如2XX響應代碼表示成功悍引,4XX表示錯誤等等恩脂。

RESTful API最常用的交互數(shù)據(jù)的格式是JSON和XML。我們將為項目構(gòu)建一個JSON序列化的REST API趣斤。我們的API會提供以下功能:

  • 檢索主題
  • 檢索可用的課程
  • 檢索課程內(nèi)容
  • 報名參加課程

我們可以通過Django創(chuàng)建自定義視圖俩块,從頭開始構(gòu)建API。但是有很多第三方模塊可以簡化創(chuàng)建API浓领,其中最流行的是Django Rest Framework玉凯。

12.1.1 安裝Django Rest Framework

Django Rest Framework可以很容易的為項目構(gòu)建REST API。你可以在這里查看所有文檔镊逝。

打開終端壮啊,使用以下命令安裝框架:

pip install djangorestframework

編輯educa項目的settings.py文件歹啼,在INSTALLED_APPS設置中添加rest_framework

INSTALLED_APPS = [
    # ...
    'rest_framework',
]

然后在settings.py文件中添加以下代碼:

REST_FRAMEWORK = {
    'DEFAULT_PREMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ]
}

你可以使用REST_FRAMEWORK設置為API提供一個特定配置。REST Framework提供了大量設置來配置默認行為浴滴。DEFAULT_PREMISSION_CLASSES設置指定讀取微王,創(chuàng)建炕倘,更新或者刪除對象的默認權(quán)限啊央。我們設置DjangoModelPermissionsOrAnonReadOnly是唯一的默認權(quán)限類瓜饥。這個類依賴Django的權(quán)限系統(tǒng)乓土,允許用戶創(chuàng)建坎炼,更新或刪除對象谣光,同時為匿名用戶提供只讀訪問蟀悦。之后你會學習更多關(guān)于權(quán)限的內(nèi)容日戈。

你可以訪問這里查看完整的REST Framework可用設置列表。

12.1.2 定義序列化器

設置REST Framework之后弯屈,我們需要指定如何序列化我們的數(shù)據(jù)资厉。輸出數(shù)據(jù)必須序列化為指定格式湘捎,輸入數(shù)據(jù)會反序列化處理消痛。框架為單個類構(gòu)建序列化器提供了以下類:

  • Serializer:為普通Python類實例提供序列化
  • ModelSerializer:為模型實例提供序列化
  • HyperlinkedModelSerializer:與ModelSerializer一樣纱新,但使用鏈接而不是主鍵表示對象關(guān)系

讓我們構(gòu)建第一個序列化器。在courses應用目錄中創(chuàng)建以下文件結(jié)構(gòu):

api/
    __init__.py
    serializers.py

我們會在api目錄中構(gòu)建所有API功能簿废,保持良好的文件結(jié)構(gòu)族檬。編輯serializers.py文件,并添加以下代碼:

from rest_framework import serializers
from ..models import Subject

class SubjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subject
        fields = ('id', 'title', 'slug')

這是Subject模型的序列化器。序列化器的定義類似于Django的FormModelForm類换怖。Meta類允許你指定序列化的模型和序列化中包括的字段。如果沒有設置fields屬性,則會包括所有模型字段抬探。

讓我們試試序列化器线梗。打開終端執(zhí)行python manage.py shell命令,然后執(zhí)行以下代碼:

>>> from courses.models import Subject
>>> from courses.api.serializers import SubjectSerializer
>>> subject = Subject.objects.latest('id')
>>> serializer = SubjectSerializer(subject)
>>> serializer.data

在這個例子中烤咧,我們獲得一個Subject對象,創(chuàng)建一個SubjectSerializer實例昌阿,然后訪問序列化的數(shù)據(jù)谣沸。你會看到以下輸出:

{'id': 4, 'slug': 'mathematics', 'title': 'Mathematics'}

正如你所看到的鳄抒,模型數(shù)據(jù)轉(zhuǎn)換為Python的原生數(shù)據(jù)類型。

12.1.3 理解解析器和渲染器

在HTTP響應中返回序列化的數(shù)據(jù)之前,需要把它渲染為特定格式辜膝。同樣的清焕,當你獲得HTTP請求時滚停,在你操作它之前键畴,需要解析傳入的數(shù)據(jù)并反序列化數(shù)據(jù)。REST Framework包括渲染器和解析器來處理這些操作。

讓我們看看如何解析收到的數(shù)據(jù)起惕。給定一個JSON字符串輸入涡贱,你可以使用REST Framework提供的JSONParser類轉(zhuǎn)換為Python對象。在Python終端中執(zhí)行以下代碼:

from io import BytesIO
from rest_framework.parsers import JSONParser
data = b'{"id":4,"title":"Music","slug":"music"}'
JSONParser().parse(BytesIO(data))

你會看到以下輸出:

{'id': 4, 'title': 'Music', 'slug': 'music'}

REST Framework還包括Renderer類惹想,允許你格式化API響應问词。框架通過內(nèi)容協(xié)商決定使用哪個渲染器嘀粱。它檢查請求的Accept頭,決定響應期望的內(nèi)容類型豌鸡。根據(jù)情況瞻赶,渲染器由URL格式后綴確定豆混。例如奠滑,觸發(fā)JSONRenderer的訪問會返回JSON響應淆攻。

回到終端執(zhí)行以下代碼,從上一個序列化器例子中渲染serializer對象:

>>> from rest_framework.renderers import JSONRenderer
>>> JSONRenderer().render(serializer.data)

你會看到以下輸出:

b'{"id":4,"title":"Mathematics","slug":"mathematics"}'

我們使用JSONRenderer渲染序列化的數(shù)據(jù)位JSON胸遇。默認情況下,REST Framework使用兩個不同的渲染器:JSONRendererBrowsableAPIRenderer。后者提供一個web接口,可以很容易的瀏覽你的API。你可以在REST_FRAMEWORK設置的DEFAULT_RENDERER_CLASSES選項中修改默認的渲染器類济欢。

你可以查看更多關(guān)于渲染器解析器的信息酱鸭。

12.1.4 構(gòu)建列表和詳情視圖

REST Framework自帶一組構(gòu)建API的通用視圖和mixins。它們提供了檢索羡儿,創(chuàng)建,更新或刪除模型對象的功能。你可以在這里查看REST Framework提供的所有通用的mixins和視圖。

讓我們創(chuàng)建檢索Subject對象的列表和詳情視圖毁渗。在courses/api/目錄中創(chuàng)建views.py文件,并添加以下代碼:

from rest_framework import generics
from ..models import Subject
from .serializers import SubjectSerializer

class SubjectListView(generics.ListAPIView):
    queryset = Subject.objects.all()
    serializer_class = SubjectSerializer

class SubjectDetailView(generics.RetrieveAPIView):
    queryset = Subject.objects.all()
    serializer_class = SubjectSerializer

在這段代碼中赎线,我們使用了REST Framework的通用ListAPIViewRetrieveAPIView廷没。我們在詳情視圖中包括一個pk URL參數(shù),來檢索給定主鍵的對象垂寥。兩個視圖都包括以下屬性:

  • queryset:用于檢索對象的基礎QuerySet颠黎。
  • serializer_class:序列化對象的類。

讓我們?yōu)橐晥D添加URL模式滞项。在courses/api/目錄中創(chuàng)建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模式:

urlpatterns = [
    # ...
    url(r'^api/', include('courses.api.urls', namespace='api')),
]

我們?yōu)锳PI的URL使用api命名空間蓖扑。使用python manage.py runserver啟動開發(fā)服務器唉铜。打開終端,并使用curl獲取http://127.0.0.1:8000/api/subjects/

bogon:educa lakerszhy$ 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潭流,請在這里下載。除了curl柜去,你還可以使用其它工具發(fā)送自定義HTTP請求灰嫉,比如瀏覽器擴展Postman,你可以在這里下載Postman嗓奢。

在瀏覽器中打開http://127.0.0.1:8000/api/subjects/讼撒。你會看到REST Framework的可瀏覽API,如下圖所示:

這個HTML界面由BrowsableAPIRenderer渲染器提供股耽。你還可以在URL中包括id來訪問一個Subject對象的API詳情視圖根盒。在瀏覽器中打開http://127.0.0.1:8000/api/subjects/1/。你會看到單個Subject對象以JSON格式渲染物蝙。

12.1.5 創(chuàng)建嵌套的序列化器

我們將為Course模型創(chuàng)建一個序列化器炎滞。編輯api/serializers.py文件,并添加以下代碼:

from ..models import Course

class CourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = Course
        fields = ('id', 'subject', 'title', 'slug', 
            'overview', 'created', 'owner', 'modules')

讓我們看看一個Course對象是如何被序列化的诬乞。在終端執(zhí)行python manage.py shell册赛,然后執(zhí)行以下代碼:

>>> from rest_framework.renderers import JSONRenderer
>>> from courses.models import Course
>>> from courses.api.serializers import CourseSerializer
>>> course = Course.objects.latest('id')
>>> serializer = CourseSerializer(course)
>>> JSONRenderer().render(serializer.data)

你獲得的JSON對象包括我們在CourseSerializer中指定的字段。你會看到modules管理器的關(guān)聯(lián)對象被序列化為主鍵列表震嫉,如下所示:

"modules": [17, 18, 19, 20, 21, 22]

我們想包括每個單元的更多信息森瘪,所以我們需要序列化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)建或更新對象担映。

打開終端,并再創(chuàng)建一個CourseSerializer實例叫潦。使用JSONRenderer渲染序列化器的data屬性蝇完。這次,單元列表被嵌套的ModuleSerializer序列化器序列化矗蕊,如下所示:

"modules": [
    {
        "order": 0,
        "title": "Django overview",
        "description": "A brief overview about the Web Framework."
    }, 
    {
        "order": 1,
        "title": "Installing Django",
        "description": "How to install Django."
    },
    ...

你可以在這里閱讀更多關(guān)于序列化器的信息短蜕。

12.1.6 構(gòu)建自定義視圖

REST Framework提供了一個APIView類,可以在Django的View類之上構(gòu)建API功能傻咖。APIView類與View類不同朋魔,它使用REST Framework的自定義RequestResponse對象,并且處理APIException異常返回相應的HTTP響應卿操。它還包括一個內(nèi)置的認證和授權(quán)系統(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視圖處理用戶報名參加課程扇雕。上面的代碼完成以下任務:

  • 我們創(chuàng)建了一個繼承自APIView的自定義視圖拓售。
  • 我們?yōu)镻OST操作定義了post()方法。這個視圖不允許其它HTTP方法镶奉。
  • 我們期望URL參數(shù)pk包含課程ID础淤。我們用給定的pk參數(shù)檢索課程,如果沒有找到則拋出404異常哨苛。
  • 我們添加當前對象到Course對象的多對多關(guān)系students中鸽凶,并返回成功的響應。

編輯api/urls.py文件建峭,并為CourseEnrollView視圖添加URL模式:

url(r'^courses/(?P<pk>\d+)/enroll/$', views.CourseEnrollView.as_view(), name='course_enroll'),

理論上玻侥,我們現(xiàn)在可以執(zhí)行一個POST請求,為當前用戶報名參加一個課程迹缀。但是使碾,我們需要識別用戶,并阻止未認證用戶訪問這個視圖祝懂。讓我們看看API認證和權(quán)限是如何工作的票摇。

12.1.7 處理認證

REST Framework提供了識別執(zhí)行請求用戶的認證類。如果認證成功砚蓬,框架會在request.user中設置認證的User對象矢门。否則設置為Django的AnonymousUser實例。

REST Framework提供以下認證后臺:

  • BasicAuthentication:HTTP基礎認證灰蛙∷钐蓿客戶端用Base64在Authorization HTTP頭中發(fā)送用戶和密碼。你可以在這里進一步學習摩梧。
  • TokenAuthentication:基于令牌的認證物延。一個Token模型用于存儲用戶令牌。用戶在Authorization HTTP頭中包括用于認證的令牌仅父。
  • SessionAuthentication:使用Django的會話后臺用于認證叛薯。當執(zhí)行從你的網(wǎng)站前端到API的AJAX請求時,這個后臺非常有用笙纤。

你可以通過繼承REST Framework提供的BaseAuthentication類耗溜,并覆寫authenticate()方法來構(gòu)建自定義認證后臺。

你可以基于單個視圖設置認證省容,或者用DEFAULT_AUTHENTICATION_CLASSES設置為全局認證抖拴。

認證只識別執(zhí)行請求的用戶。它不會允許或阻止訪問視圖腥椒。你必須使用權(quán)限來顯示訪問視圖阿宅。

你可以在這里查看所有關(guān)于認證的信息候衍。

讓我們添加BasicAuthentication到我們的視圖。編輯courses應用的api/views.py文件家夺,并添加authentication_classes屬性到CourseEnrollView

from rest_framework.authentication import BasicAuthentication

class CourseEnrollView(APIView):
    authentication_classes = (BasicAuthentication, )
    # ...

用戶將通過設置在HTTP請求中的Authorization頭的證書識別脱柱。

12.1.8 添加權(quán)限到視圖

REST Framework包括一個權(quán)限系統(tǒng)伐弹,用于限制視圖的訪問拉馋。REST Framework的一些內(nèi)置權(quán)限是:

  • AllowAny:不限制訪問,不管用戶是否認證惨好。
  • IsAuthenticated:只允許認證的用戶訪問煌茴。
  • IsAuthenticatedOrReadOnly:認證用戶可以完全訪問。匿名用戶只允許執(zhí)行讀取方法日川,比如GET蔓腐,HEAD或OPTIONS。
  • DjangoModelPermissions:捆綁到django.contrib.auth的權(quán)限龄句。視圖需要一個queryset屬性回论。只有分配了模型權(quán)限的認證用戶才能獲得權(quán)限。
  • DjangoObjectPermissions:基于單個對象的Django權(quán)限分歇。

如果用戶被拒絕訪問傀蓉,他們通常會獲得以下某個HTTP錯誤代碼:

  • HTTP 401:未認證
  • HTTP 403:沒有權(quán)限

你可以在這里閱讀更多關(guān)于權(quán)限的信息。

編輯courses應用的api/views.py文件职抡,并在CourseEnrollView中添加permission_classes屬性:

from rest_framework.authentication import BasicAuthentication
from rest_framework.permissions import IsAuthenticated

class CourseEnrollView(APIView):
    authentication_classes = (BasicAuthentication, )
    permission_classes = (IsAuthenticated, )
    # ..

我們引入了IsAuthenticated權(quán)限葬燎。這會阻止匿名用戶訪問這個視圖。現(xiàn)在我們可以執(zhí)行POST請求到新的API方法缚甩。

確保開發(fā)服務器正在運行谱净。打開終端并執(zhí)行以下命令:

curl -i -X POST http://127.0.0.1:8000/api/courses/1/enroll/

你會獲得以下響應:

HTTP/1.0 401 UNAUTHORIZED
...
{"detail": "Authentication credentials were not provided."}

因為我們是未認證用戶,所以如期獲得401 HTTP代碼擅威。讓我們用其中一個用戶進行基礎認證壕探。執(zhí)行以下命令:

curl -i -X POST -u student:password http://127.0.0.1:8000/api/courses/1/enroll/

用已存在用戶憑證替換student:password。你會獲得以下響應:

HTTP/1.0 200 OK
...
{"enrolled": true}

你可以訪問管理站點郊丛,檢查用戶是否報名參加課程李请。

12.1.9 創(chuàng)建視圖集和路由

ViewSets允許你定義你的API交互,并讓REST Framework用Router對象動態(tài)構(gòu)建URL宾袜。通過視圖集捻艳,你可以避免多個視圖的重復邏輯。視圖集包括典型的創(chuàng)建庆猫,檢索认轨,更新,刪除操作月培,分別是list()嘁字,create()恩急,retrieve()update()纪蜒,partial_update()destroy()衷恭。

讓我們?yōu)?code>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

我們從ReadOnlyModelViewSet繼承随珠,它提供了只讀操作list()retrieve(),用于列出對象或檢索單個對象猬错。編輯api/urls.py文件窗看,并為我們的視圖集創(chuàng)建一個路由:

from django.conf.urls import url, include
from . import views
from rest_framework import routers

router = routers.DefaultRouter()
router.register('courses', views.CourseViewSet)

urlpatterns = [
    # ...
    url(r'^', include(router.urls)),
]

我們創(chuàng)建了一個DefaultRouter對象,并用courses前綴注冊我們的視圖集倦炒。路由負責為我們的視圖集自動生成URL显沈。

在瀏覽器中打開http://127.0.0.1:8000/api/。你會看到路由在它的基礎URL中列出所有視圖集逢唤,如下圖所示:

你現(xiàn)在可以訪問http://127.0.0.1:8000/api/courses/檢索課程列表拉讯。

你可以在這里進一步學習視圖集。你還可以在這里查看更多關(guān)于路由的信息鳖藕。

12.1.10 添加額外操作到視圖集

你可以添加額外操作到視圖集中魔慷。讓我們把之前的CourseEnrollView視圖為一個自定義視圖集操作。編輯api/views.py文件吊奢,并修改CourseViewSet類:

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})

我們添加了一個自定義的enroll()方法盖彭,它代表這個視圖集的一個額外操作。上面的代碼執(zhí)行以下任務:

  • 我們使用框架的detail_route裝飾器页滚,指定這是在單個對象上執(zhí)行的操作召边。
  • 裝飾器允許我們?yōu)椴僮魈砑幼远x屬性。我們指定這個視圖只允許POST方法裹驰,并設置了認證和權(quán)限類隧熙。
  • 我們使用self.get_object()檢索Courses對象。
  • 我們把當前用戶添加到students多對多關(guān)系中幻林,并返回一個自定義的成功響應贞盯。

編輯api/urls.py文件,移除以下URL沪饺,因為我們不再需要它:

url(r'^courses/(?P<pk>\d+)/enroll/$', views.CourseEnrollView.as_view(), name='course_enroll'),

然后編輯api/views.py文件躏敢,移除CourseEnrollView類。

現(xiàn)在整葡,報名參加課程的URL由路由自動生成件余。因為它使用操作名enroll,所以URL保持不變。

12.1.11 創(chuàng)建自定義權(quán)限

我們希望學生可以訪問它們報名的課程內(nèi)容啼器。只有報名的學生才可以訪問課程內(nèi)容旬渠。最好的實現(xiàn)方式是使用一個自定義權(quán)限類。Django提供的BasePermission類允許你定義以下方法:

  • has_permission():視圖級別的權(quán)限檢查
  • has_object_permission():實例級別的權(quán)限檢查

如果獲得訪問權(quán)限端壳,這些方法返回True告丢,否則返回False。在courses/api/目錄中創(chuàng)建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()

我們從BasePermission類繼承岖免,并覆寫has_object_permission()。我們檢查執(zhí)行請求的用戶是否存在Course對象的students關(guān)系中成翩。我們下一步會使用IsEnrolled權(quán)限觅捆。

12.1.12 序列化課程內(nèi)容

我們需要序列化課程內(nèi)容。Content模型包括一個通用外鍵麻敌,允許我們訪問關(guān)聯(lián)對象的不同內(nèi)容模型。但是掂摔,我們在上一章為所有內(nèi)容模型添加了通用的render()方法术羔。我們可以使用這個方法為API提供渲染后的內(nèi)容。

編輯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 Framework提供的RelatedField序列化器字段和覆寫to_representation()方法,我們定義了一個自定義字段叭披。我們?yōu)?code>Content模型定義了ContentSerializer序列化器寥殖,并用自定義字段作為item通用外鍵。

我們需要一個包括內(nèi)容的Module模型的替換序列化器涩蜘,以及一個擴展的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()操作同诫,但是包括課程內(nèi)容的視圖粤策。編輯api/views.py文件,并在CourseViewSet類中添加以下方法:

from .permissions import IsEnrolled
from .serializers import CourseWithContentsSerializer

class CourseViewSet(viewsets.ReadOnlyModelViewSet):
    # ...
    @detail_route(
        methods=['get'],
        serializer_class=CourseWithContentsSerializer,
        authentication_classes=[BasicAuthentication],
        permission_classes=[IsAuthenticated, IsEnrolled]
    )
    def contents(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

這個方法執(zhí)行以下任務:

  • 我們使用detail_route裝飾器指定該操作在單個對象上執(zhí)行误窖。
  • 我們指定該操作只允許GET方法叮盘。
  • 我們使用新的CourseWithContentsSerializer序列化器類,它包括渲染的課程內(nèi)容霹俺。
  • 我們使用IsAuthenticated和自定義的IsEnrolled權(quán)限柔吼。這樣可以確保只有報名的用戶可以訪問課程內(nèi)容。
  • 我們使用存在的retrieve()操作返回課程對象丙唧。

在瀏覽器中打開http://127.0.0.1:8000/api/courses/1/contents/愈魏。如果你用正確證書訪問視圖,你會看到課程的每個單元,包括渲染后的課程內(nèi)容的HTML蝌戒,如下所示:

{
   "order": 0,
   "title": "Installing Django",
   "description": "",
   "contents": [
        {
        "order": 0,
        "item": "<p>Take a look at the following video for installing Django:</p>\n"
        }, 
        {
        "order": 1,
        "item": "\n<iframe width=\"480\" height=\"360\" src=\"http://www.youtube.com/embed/bgV39DlmZ2U?wmode=opaque\" frameborder=\"0\" allowfullscreen></iframe>\n\n"
        } 
    ]
    }

你已經(jīng)構(gòu)建了一個簡單的API串塑,允許其它服務通過編程方式訪問course應用。REST Framework還允許你用ModelViewSet視圖集管理創(chuàng)建和編輯對象北苟。我們已經(jīng)學習了Django Rest Framework的主要部分桩匪,但你仍可以在這里進一步學習它的特性。

12.2 總結(jié)

在這一章中友鼻,你創(chuàng)建了一個RESTful API傻昙,可以讓其它服務與你的web應用交互。

額外的第十三章可以在這里下載彩扔。它教你如何使用uWSGINGINX構(gòu)建一個生產(chǎn)環(huán)境妆档。你還會學習如何實現(xiàn)一個自定義的中間件和創(chuàng)建自定義的管理命令。

你已經(jīng)到達了本書的結(jié)尾虫碉。恭喜你贾惦!你已經(jīng)學會了用Django構(gòu)建一個成功的web應用所需要的技巧。本書指導你完成開發(fā)實際項目敦捧,以及將Django與其它技術(shù)結(jié)合⌒氚澹現(xiàn)在你已經(jīng)準備好創(chuàng)建自己的Django項目,不管是一個簡單的原型還是一個大型的web應用兢卵。

祝你下一次Django冒險活動好運习瑰!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市秽荤,隨后出現(xiàn)的幾起案子甜奄,更是在濱河造成了極大的恐慌,老刑警劉巖窃款,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件课兄,死亡現(xiàn)場離奇詭異,居然都是意外死亡雁乡,警方通過查閱死者的電腦和手機第喳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來踱稍,“玉大人曲饱,你說我怎么就攤上這事≈樵拢” “怎么了扩淀?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長啤挎。 經(jīng)常有香客問我驻谆,道長卵凑,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任胜臊,我火速辦了婚禮勺卢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘象对。我一直安慰自己黑忱,他們只是感情好,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布勒魔。 她就那樣靜靜地躺著甫煞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冠绢。 梳的紋絲不亂的頭發(fā)上抚吠,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機與錄音弟胀,去河邊找鬼楷力。 笑死,一個胖子當著我的面吹牛邮利,可吹牛的內(nèi)容都是我干的弥雹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼延届,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了贸诚?” 一聲冷哼從身側(cè)響起方庭,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎酱固,沒想到半個月后械念,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡运悲,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年龄减,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片班眯。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡希停,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出署隘,到底是詐尸還是另有隱情宠能,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布磁餐,位于F島的核電站违崇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜羞延,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一渣淳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧伴箩,春花似錦入愧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至呵恢,卻和暖如春鞠值,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背渗钉。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工彤恶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鳄橘。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓声离,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瘫怜。 傳聞我的和親對象是個殘疾皇子术徊,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容