《Django By Example》第十二章(終章) 中文 翻譯 (個(gè)人學(xué)習(xí),渣翻)

epub格式下載 感謝@Cluas

鏈接: https://pan.baidu.com/s/1kVGavLd 密碼: raxg

全文鏈接

第一章 創(chuàng)建一個(gè)blog應(yīng)用
第二章 使用高級(jí)特性來(lái)增強(qiáng)你的blog
第三章 擴(kuò)展你的blog應(yīng)用
第四章上 創(chuàng)建一個(gè)社交網(wǎng)站
第四章下 創(chuàng)建一個(gè)社交網(wǎng)站
第五章 在你的網(wǎng)站中分享內(nèi)容
第六章 跟蹤用戶動(dòng)作
第七章 建立一個(gè)在線商店
第八章 管理付款和訂單
第九章上 擴(kuò)展你的商店
第九章下 擴(kuò)展你的商店
第十章上 創(chuàng)建一個(gè)在線學(xué)習(xí)平臺(tái)
第十章下 創(chuàng)建一個(gè)在線學(xué)習(xí)平臺(tái)
第十一章 緩存內(nèi)容
第十二章 構(gòu)建一個(gè)API

書(shū)籍出處:https://www.packtpub.com/web-development/django-example
原作者:Antonio Melé

(譯者注:第十二章,全書(shū)最后一章,終于到這章了。)

第十二章

構(gòu)建一個(gè)API

在上一章中,你構(gòu)建了一個(gè)學(xué)生注冊(cè)系統(tǒng)和課程報(bào)名。你創(chuàng)建了用來(lái)展示課程內(nèi)容的視圖以及如何使用Django的緩存框架。在這章中,你將學(xué)習(xí)如何做到以下幾點(diǎn):

  • 構(gòu)建一個(gè) RESTful API
  • 用API視圖操作認(rèn)證和權(quán)限
  • 創(chuàng)建API視圖放置和路由

構(gòu)建一個(gè)RESTful API

你可能想要?jiǎng)?chuàng)建一個(gè)接口給其他的服務(wù)來(lái)與你的web應(yīng)用交互。通過(guò)構(gòu)建一個(gè)API,你可以允許第三方來(lái)消費(fèi)信息以及程序化的操作你的應(yīng)用程序。

你可以通過(guò)很多方法構(gòu)成你的API,但是我們最鼓勵(lì)你遵循REST原則。REST體系結(jié)構(gòu)來(lái)自Representational State Transfer。RESTful API是基于資源的(resource-based)。你的模型代表資源和HTTP方法例如GET,POST,PUT,以及DELETE是被用來(lái)取回,創(chuàng)建,更新,以及刪除對(duì)象的。HTTP響應(yīng)代碼也可以在上下文中使用。不同的HTTP響應(yīng)代碼的返回用來(lái)指示HTTP請(qǐng)求的結(jié)果,例如,2XX響應(yīng)代碼用來(lái)表示成功,4XX表示錯(cuò)誤,等等。

在RESTful API中最通用的交換數(shù)據(jù)是JSON和XML。我們將要為我們的項(xiàng)目構(gòu)建一個(gè)JSON序列化的REST API。我們的API會(huì)提供以下功能:

  • 獲取科目
  • 獲取可用的課程
  • 獲取課程內(nèi)容
  • 課程報(bào)名

我們可以通過(guò)創(chuàng)建定制視圖從Django開(kāi)始構(gòu)建一個(gè)API。當(dāng)然,有很多第三方的模塊可以給你的項(xiàng)目簡(jiǎn)單的創(chuàng)建一個(gè)API,其中最有名的就是Django Rest Framework。

安裝Django Rest Framework

Django Rest Framework允許你為你的項(xiàng)目方便的構(gòu)建REST APIs。你可以通過(guò)訪問(wèn) http://www.django-rest-framework.org 找到所有REST Framework信息。

打開(kāi)shell然后通過(guò)以下命令安裝這個(gè)框架:

pip install djangorestframework=3.2.3

編輯educa項(xiàng)目的settings.py文件,在INSTALLED_APPS設(shè)置中添加rest_framework來(lái)激活這個(gè)應(yīng)用,如下所示:

INSTALLED_APPS = (
       # ...
       'rest_framework',
   )

之后,添加如下代碼到settings.py文件中:

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

你可以使用REST_FRAMEWORK設(shè)置為你的API提供一個(gè)指定的配置。REST Framework提供了一個(gè)廣泛的設(shè)置去配置默認(rèn)的行為。DEFAULT_PERMISSION_CLASSES配置指定了去讀取,創(chuàng)建,更新或者刪除對(duì)象的默認(rèn)權(quán)限。我們?cè)O(shè)置DjangoModelPermissionsOrAnonReadOnly作為唯一的默認(rèn)權(quán)限類。這個(gè)類依賴與Django的權(quán)限系統(tǒng)允許用戶去創(chuàng)建,更新,或者刪除對(duì)象,同時(shí)提供只讀的訪問(wèn)給陌生人用戶。你會(huì)在之后學(xué)習(xí)更多關(guān)于權(quán)限的方面。

如果要找到一個(gè)完整的REST框架可用設(shè)置列表,你可以訪問(wèn) http://www.django-rest-framework.org/api-guide/settings/

定義序列化器

設(shè)置好REST Framework之后,我們需要指定我們的數(shù)據(jù)將會(huì)如何序列化。輸出的數(shù)據(jù)必須被序列化成指定的格式,并且輸出的數(shù)據(jù)將會(huì)給進(jìn)程去序列化。REST框架提供了以下類來(lái)給單個(gè)對(duì)象去構(gòu)建序列化:

  • Serializer:給一般的Python類實(shí)例提供序列化。
  • ModelSerializer:給模型實(shí)例提供序列化。
  • HyperlinkedModelSerializer:類似與ModelSerializer,但是代表與鏈接而不是主鍵的對(duì)象關(guān)系。

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

api/
    __init__.py
    serializers.py

我們將會(huì)在api目錄中構(gòu)建所有的API功能為了保持一切都有良好的組織。編輯serializeers.py文件,然后添加以下代碼:

from rest_framework import serializers
from ..models import Subject

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

以上是給Subject模型使用的序列化器。序列化器以一種類似的方式被定義給Django
FromModelForm類。Meta類允許你去指定模型序列化以及給序列化包含的字段。所有的模型字段都會(huì)被包含如果你沒(méi)有設(shè)置一個(gè)fields屬性。

讓我們嘗試我們的序列化器。打開(kāi)命令行通過(guò)`python manage.py shell*開(kāi)始Django shell。運(yùn)行以下代碼:

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

在上面的例子中,我們拿到了一個(gè)Subject對(duì)象,創(chuàng)建了一個(gè)SubjectSerializer的實(shí)例,并且訪問(wèn)序列化的數(shù)據(jù)。你會(huì)得到以下輸出:

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

如你所見(jiàn),模型數(shù)據(jù)被轉(zhuǎn)換成了Python的數(shù)據(jù)類型。

了解解析器和渲染器

在你在一個(gè)HTTP響應(yīng)中返回序列化數(shù)據(jù)之前,這個(gè)序列化數(shù)據(jù)必須使用指定的格式進(jìn)行渲染。同樣的,當(dāng)你拿到一個(gè)HTTP請(qǐng)求,在你使用這個(gè)數(shù)據(jù)操作之前你必須解析傳入的數(shù)據(jù)并且反序列化這個(gè)數(shù)據(jù)。REST Framework包含渲染器和解析器來(lái)執(zhí)行以上操作。

讓我們看下如何解析傳入的數(shù)據(jù)。給予一個(gè)JSON字符串輸入,你可以使用REST康佳提供的JSONParser類來(lái)轉(zhuǎn)變它成為一個(gè)Python對(duì)象。在Python shell中執(zhí)行以下代碼:

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

你將會(huì)拿到以下輸出:

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

REST Framework還包含Renderer類,該類允許你去格式化API響應(yīng)。框架會(huì)查明通過(guò)的內(nèi)容使用的是哪種渲染器。它對(duì)響應(yīng)進(jìn)行檢查,根據(jù)請(qǐng)求的Accept頭去預(yù)判內(nèi)容的類型。除此以外,渲染器可以通過(guò)URL的格式后綴進(jìn)行預(yù)判。舉個(gè)例子,訪問(wèn)將會(huì)出發(fā)JSONRenderer為了返回一個(gè)JSON響應(yīng)。

回到shell中,然后執(zhí)行以下代碼去從提供的序列化器例子中渲染serializer對(duì)象:

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

你會(huì)看到以下輸出:

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

我們使用JSONRenderer去渲染序列化數(shù)據(jù)為JSON。默認(rèn)的,REST Framework使用兩種不同的渲染器:JSONRendererBrowsableAPIRenderer。后者提供一個(gè)web接口可以方便的瀏覽你的API。你可以通過(guò)REST_FRAMEWORK設(shè)置的DEFAULT_RENDERER_CLASSES選項(xiàng)改變默認(rèn)的渲染器類。

你可以找到更多關(guān)于渲染器和解析器的信息通過(guò)訪問(wèn) http://www.django-rest-framework.org/api-guide/renderers/ 以及 http://www.django-rest- framework.org/api-guide/parsers/

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

REST Framework自帶一組通用視圖和mixins,你可以用來(lái)構(gòu)建你自己的API。它們提供了獲取,創(chuàng)建,更新以及刪除模型對(duì)象的功能。你可以看到所有REST Framework提供的通用mixins和視圖,通過(guò)訪問(wèn) http://www.django-rest-framework.org/api-guide/generic-views/

讓我們創(chuàng)建列表和詳情視圖去取回Subject對(duì)象們。在courses/api/目錄下創(chuàng)建一個(gè)新的文件并命名為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視圖。我們給給予的關(guān)鍵值包含了一個(gè)pk URL參數(shù)給詳情視圖去取回對(duì)象。兩個(gè)視圖都有以下屬性:

  • queryset:基礎(chǔ)查詢集用來(lái)取回對(duì)象。
  • serializer_class:這個(gè)類用來(lái)序列化對(duì)象。

讓我們給我們的視圖添加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項(xiàng)目的主urls.py文件并且包含以下API模式:

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

我們給我們的API URLs使用api命名空間。確保你的服務(wù)器已經(jīng)通過(guò)命令python manange.py runserver啟動(dòng)。打開(kāi)shell然后通過(guò)cURL獲取URL http://127.0.0.1:8000/api/subjects/ 如下所示:

$ curl http://127.0.0.1:8000/api/subjects/

你會(huì)獲取類似以下的響應(yīng):

[{"id":2,"title":"Mathematics","slug":"mathematics"},{"id":4,"title":"Music","slug":"music"},{"id":3,"title":"Physics","slug":"physics"},{"id":1,"title":"Programming","slug":"programming"}]

這個(gè)HTTP響應(yīng)包含JSON格式的一個(gè)Subject對(duì)象列。如果你的操作系統(tǒng)沒(méi)有安裝過(guò)cURL,你可還可以使用其他的工具去發(fā)送定制HTTP請(qǐng)求例如一個(gè)瀏覽器擴(kuò)展 Postman ,這個(gè)擴(kuò)展你可以在 https://www.getpostman.com 找到。

在你的瀏覽器中打開(kāi) http://127.0.0.1:8000/api/subjects/ 。你會(huì)看到如下所示的REST Framework的可瀏覽API:

django-12-1

這個(gè)HTML界面由BrowsableAPIRenderer渲染器提供。它展示了結(jié)果頭和內(nèi)容并且允許執(zhí)行請(qǐng)求。你還可以在URL包含一個(gè)Subject對(duì)象的id來(lái)訪問(wèn)該對(duì)象的API詳情視圖。在你的瀏覽器中打開(kāi) http://127.0.0.1:8000/api/subjects/1/ 。你將會(huì)看到一個(gè)單獨(dú)的渲染成JSON格式的Subject對(duì)象。

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

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

from ..models import Course

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

讓我們看下一個(gè)Course對(duì)象是如何被序列化的。打開(kāi)shell,運(yùn)行python manage.py shell,然后運(yùn)行以下代碼:

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)

你將會(huì)通過(guò)我們包含在CourseSerializer中的字段獲取到一個(gè)JSON對(duì)象。你可以看到modules管理器的被關(guān)聯(lián)對(duì)象唄序列化成一列關(guān)鍵值,如下所示:

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

我們想要包含個(gè)多的信息關(guān)于每一個(gè)模塊,所以我們需要序列化Module對(duì)象以及嵌套它們。修改api/serializers.py文件提供的代碼,使之看上去如下所示:

from rest_framework import serializers
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')

我們給Module模型定義了一個(gè)ModuleSerializer去提供序列化。之后我們添加一個(gè)modules屬性給CourseSerializer去嵌套ModuleSerializer序列化器。我們?cè)O(shè)置many=True去表明我們正在序列化多個(gè)對(duì)象。read_only參數(shù)表明這個(gè)字段是只讀的并且不可以被包含在任何輸入中去創(chuàng)建或者升級(jí)對(duì)象。

打開(kāi)shell并且再次創(chuàng)建一個(gè)CourseSerializer的實(shí)例。使用JSONRenderer渲染序列化器的data屬性。這一次,被排列的模塊會(huì)被通過(guò)嵌套的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)于序列化器的內(nèi)容,通過(guò)訪問(wèn) http://www.django-rest-framework.org/api-guide/serializers/

構(gòu)建定制視圖

REST Framework提供一個(gè)APIView類,這個(gè)類基于Django的View類構(gòu)建API功能。APIView類與View在使用REST Framework的定制Request以及Response對(duì)象時(shí)不同,并且操作APIException例外的返回合適的HTTP響應(yīng)。它還有一個(gè)內(nèi)建的驗(yàn)證和認(rèn)證系統(tǒng)去管理視圖的訪問(wèn)。

我們將要?jiǎng)?chuàng)建一個(gè)視圖給用戶去對(duì)課程進(jìn)行報(bào)名。編輯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視圖操縱用戶對(duì)課程進(jìn)行報(bào)名。以上代碼解釋如下:

  • 我們創(chuàng)建了一個(gè)定制視圖,是APIView的子類。
  • 我們給POST操作定義了一個(gè)post()方法。其他的HTTP方法都不允許放這個(gè)這個(gè)視圖。
  • 我們預(yù)計(jì)一個(gè)pkURL參數(shù)會(huì)博涵一個(gè)課程的ID。我們通過(guò)給予的pk參數(shù)獲取這個(gè)課程,并且如果這個(gè)不存在的話就拋出一個(gè)404異常。
  • 我們添加當(dāng)前用戶給Course對(duì)象的students多對(duì)多關(guān)系并放回一個(gè)成功響應(yīng)。

編輯api/urls.py文件并且給CourseEnrollView視圖添加以下URL模式:

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

理論上,我們現(xiàn)在可以執(zhí)行一個(gè)POST請(qǐng)求去給當(dāng)前用戶對(duì)一個(gè)課程進(jìn)行報(bào)名。但是,我們需要辨認(rèn)這個(gè)用戶并且阻止為認(rèn)證的用戶來(lái)訪問(wèn)這個(gè)視圖。讓我們看下API認(rèn)證和權(quán)限是如何工作的。

操縱認(rèn)證

REST Framework提供認(rèn)證類去辨別用戶執(zhí)行的請(qǐng)求。如果認(rèn)證成功,這個(gè)框架會(huì)在request.user中設(shè)置認(rèn)證的User對(duì)象。如果沒(méi)有用戶被認(rèn)證,一個(gè)Django的AnonymousUser實(shí)例會(huì)被代替。

REST Framework提供以下認(rèn)證后臺(tái):

  • BasicAuthentication:HTTP基礎(chǔ)認(rèn)證。用戶和密碼會(huì)被編譯為Base64并被客戶端設(shè)置在Authorization HTTP頭中。你可以學(xué)習(xí)更多關(guān)于它的內(nèi)容,通過(guò)訪問(wèn) https://en.wikipedia.org/wiki/Basic_access_authentication
  • TokenAuthentication:基于token的認(rèn)證。一個(gè)Token模型被用來(lái)存儲(chǔ)用戶tokens。用來(lái)認(rèn)證的Authorization HTTP頭里面擁有包含token的用戶。
  • SessionAuthentication:使用Djnago的會(huì)話后臺(tái)(session backend)來(lái)認(rèn)證。這個(gè)后臺(tái)從你的網(wǎng)站前端來(lái)執(zhí)行認(rèn)證AJAX請(qǐng)求給API是非常有用的。

你可以創(chuàng)建一個(gè)通過(guò)繼承REST Framework提供的BaseAuthentication類的子類以及重寫(xiě)authenticate()方法來(lái)構(gòu)建一個(gè)定制的認(rèn)證后臺(tái)。

你可以在每個(gè)視圖的基礎(chǔ)上設(shè)置認(rèn)證,或者通過(guò)DEFAULT_AUTHENTICATION_CLASSES設(shè)置為全局認(rèn)證。

認(rèn)證只能失敗用戶正在執(zhí)行的請(qǐng)求。它無(wú)法允許或者組織視圖的訪問(wèn)。你必須使用權(quán)限去限制視圖的訪問(wèn)。

你可以找到關(guān)于認(rèn)證的所有信息,通過(guò)訪問(wèn) http://www.django-rest- framework.org/api-guide/authentication/

讓我們給我們的視圖添加BasicAuthentication。編輯courses應(yīng)用的api/views.py文件,然后給CourseEnrollView添加一個(gè)authentication_classes屬性,如下所示:

from rest_framework.authentication import BasicAuthentication

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

用戶將會(huì)被設(shè)置在HTTP請(qǐng)求中的Authorization頭里面的證書(shū)進(jìn)行識(shí)別。

給視圖添加權(quán)限

REST Framework包含一個(gè)權(quán)限系統(tǒng)去限制視圖的訪問(wèn)。一些REST Framework的內(nèi)置權(quán)限如下所示:

  • AllowAny:無(wú)限制的訪問(wèn),無(wú)論當(dāng)前用戶是否通過(guò)認(rèn)證。
  • IsAuthenticated:只允許通過(guò)認(rèn)證的用戶。
  • IsAuthenticatedOrReadOnly:通過(guò)認(rèn)證的用戶擁有完整的權(quán)限。陌生用戶只允許去還行可讀的方法,例如GET, HEAD或者OPETIONS。
  • DjangoModelPermissions:權(quán)限與django.contrib.auth進(jìn)行了捆綁。視圖需要一個(gè)queryset屬性。只有分配了模型權(quán)限的并經(jīng)過(guò)認(rèn)證的用戶才能獲得權(quán)限。
  • DjangoObjectPermissions:基于每個(gè)對(duì)象基礎(chǔ)上的Django權(quán)限。

如果用戶沒(méi)有權(quán)限,他們通常會(huì)獲得以下某個(gè)HTTP錯(cuò)誤:

  • HTTP 401:無(wú)認(rèn)證。
  • HTTP 403:沒(méi)有權(quán)限。

你可以獲得更多的關(guān)于權(quán)限的信息,通過(guò)訪問(wèn) http://www.django-rest- framework.org/api-guide/permissions/

編輯courses應(yīng)用的api/views.py文件然后給CourseEnrollView添加一個(gè)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)限。這個(gè)權(quán)限將會(huì)組織陌生用戶訪問(wèn)這個(gè)視圖。現(xiàn)在,我們可以之sing一個(gè)POST請(qǐng)求給我們的新的API方法。

確保開(kāi)發(fā)服務(wù)器正在運(yùn)行。打開(kāi)shell然后運(yùn)行以下命令:

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

你將會(huì)得到以下響應(yīng):

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

如我們所預(yù)料的,我們得到了一個(gè)401 HTTP code,因?yàn)槲覀儧](méi)有認(rèn)證過(guò)。讓我們帶上我們的一個(gè)用戶進(jìn)行下基礎(chǔ)認(rèn)證。運(yùn)行以下命令:

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

使用一個(gè)已經(jīng)存在的用戶的證書(shū)替換student:password。你會(huì)得到以下響應(yīng):

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

你可以額訪問(wèn)管理站點(diǎn)然后檢查到上面命令中的用戶已經(jīng)完成了課程的報(bào)名。

創(chuàng)建視圖設(shè)置和路由

ViewSets允許你去定義你的API的交互并且讓REST Framework通過(guò)一個(gè)Router對(duì)象動(dòng)態(tài)的構(gòu)建URLs。通過(guò)使用視圖設(shè)置,你可以避免給多個(gè)視圖重復(fù)編寫(xiě)相同的邏輯。視圖設(shè)置包含典型的創(chuàng)建,獲取,更新,刪除選項(xiàng)操作,它們是 list(),create(),retrieve(),update(),partial_update()以及destroy()

讓我們給Course模型創(chuàng)建一個(gè)視圖設(shè)置。編輯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)建了一個(gè)繼承ReadOnlyModelViewSet類的子類,被繼承的類提供了只讀的操作 list()retrieve(),前者用來(lái)排列對(duì)象,后者用來(lái)取回一個(gè)單獨(dú)的對(duì)象。編輯api/urls.py文件并且給我們的視圖設(shè)置創(chuàng)建一個(gè)路由,如下所示:

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'^', include(router.urls)),
]

我們創(chuàng)建兩個(gè)一個(gè)DefaultRouter對(duì)象并且通過(guò)courses前綴注冊(cè)了我們的視圖設(shè)置。這個(gè)路由負(fù)責(zé)給我們的視圖動(dòng)態(tài)的生成URLs。

在你的瀏覽器中打開(kāi) http://127.0.0.1:8000/api/ 。你會(huì)看到路由排列除了所有的視圖設(shè)置在它的基礎(chǔ)URL中,如下圖所示:

django-12-2

你可以訪問(wèn) http://127.0.0.1:8000/api/courses/ 去獲取課程的列表。

你可以學(xué)習(xí)到跟多關(guān)于視圖設(shè)置的內(nèi)容,通過(guò)訪問(wèn) http://www.django-rest-framework.org/api-guide/viewsets/ 。你也可以找到更多關(guān)于路由的信息,通過(guò)訪問(wèn) http://www.django-rest-framework.org/api-guide/routers/

給視圖設(shè)置添加額外的操作

你可以給視圖設(shè)置添加額外的操作。讓我們修改我們之前的CourseEnrollView視圖成為一個(gè)定制的視圖設(shè)置操作。編輯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})

我們添加了一個(gè)定制enroll()方法相當(dāng)于給這個(gè)視圖設(shè)置的一個(gè)額外的操作。以上的代碼解釋如下:

  • 我們使用框架的detail_route裝飾器去指定這個(gè)類是一個(gè)在一個(gè)單獨(dú)對(duì)象上被執(zhí)行的操作。
  • 這個(gè)裝飾器允許我們給這個(gè)操作添加定制屬性。我們指定這個(gè)視圖只允許POST方法,并且設(shè)置了認(rèn)證和權(quán)限類。
  • 我們使用self.get_object()去獲取Course對(duì)象。
  • 我們給students添加當(dāng)前用戶的多對(duì)多關(guān)系并且返回一個(gè)定制的成功響應(yīng)。

編輯api/urls.py文件并移除以下URL,因?yàn)槲覀儾辉傩枰鼈儯?/p>

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

之后編輯api/views.py文件并且移除CourseEnrollView類。

這個(gè)用來(lái)在課程中報(bào)名的URL現(xiàn)在已經(jīng)是由路由動(dòng)態(tài)的生成。這個(gè)URL保持不變,因?yàn)樗褂梦覀兊牟僮髅?em>enroll動(dòng)態(tài)的進(jìn)行構(gòu)建。

創(chuàng)建定制權(quán)限

我們想要學(xué)生可以訪問(wèn)他們報(bào)名過(guò)的課程的內(nèi)容。只有在這個(gè)課程中報(bào)名過(guò)的學(xué)生才能訪問(wèn)這個(gè)課程的內(nèi)容。最好的方法就是通過(guò)一個(gè)定制的權(quán)限類。Django提供了一個(gè)BasePermission類允許你去定制以下功能:

  • has_permission():視圖級(jí)的權(quán)限檢查。
  • has_object_permission():實(shí)例級(jí)的權(quán)限檢查。

以上方法會(huì)返回True來(lái)允許訪問(wèn),相反就會(huì)返回False。在courses/api/中創(chuàng)建一個(gè)新的文件并命名為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)建了一個(gè)繼承BasePermission類的子類,并且重寫(xiě)了has_object_permission()。我們檢查執(zhí)行請(qǐng)求的用戶是否存在Course對(duì)象的students關(guān)系。我們下一步將要使用IsEnrolled權(quán)限。

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

我們需要序列化課程內(nèi)容。Content模型包含一個(gè)通用的外鍵允許我們?nèi)リP(guān)聯(lián)不同的內(nèi)容模型對(duì)象。然而,我們給上一章中給所有的內(nèi)容模型添加了一個(gè)公用的render()方法。我們可以使用這個(gè)方法去提供渲染過(guò)的內(nèi)容給我們的API。

編輯courses應(yīng)用的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')

在以上代碼中,我們通過(guò)子類化REST Framework提供的RealtedField序列化器字段定義了一個(gè)定制字段并且重寫(xiě)了to_representation()方法。我們給Content模型定義了ContentSerializer序列化器并且使用定制字段給item生成外鍵。

我們需要一個(gè)替代序列化器給Module模型來(lái)包含它的內(nèi)容,以及一個(gè)擴(kuò)展的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)建一個(gè)視圖來(lái)模仿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)

以上的方法解釋如下:

  • 我們使用detail_route裝飾器去指定這個(gè)操作是在一個(gè)單獨(dú)的對(duì)象上進(jìn)行執(zhí)行。
  • 我們指定只有GET方法允許訪問(wèn)這個(gè)操作。
  • 我們使用新的CourseWithContentsSerializer序列化器類來(lái)包含渲染過(guò)的課程內(nèi)容。
  • 我們使用IsAuthenticated和我們的定制IsEnrolled權(quán)限。只要做到了這點(diǎn),我們可以確保只有在這個(gè)課程中報(bào)名的用戶才能訪問(wèn)這個(gè)課程的內(nèi)容。
  • 我們使用存在的retrieve()操作去返回課程對(duì)象。

在你的瀏覽器中打開(kāi) http://127.0.0.1:8000/api/courses/1/contents/ 。如果你使用正確的證書(shū)訪問(wèn)這個(gè)視圖,你會(huì)看到這個(gè)課程的每一個(gè)模塊都包含給渲染過(guò)的課程內(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)建了一個(gè)簡(jiǎn)單的API允許其他服務(wù)器來(lái)程序化的訪問(wèn)課程應(yīng)用。REST Framework還允許你通過(guò)ModelViewSet視圖設(shè)置去管理創(chuàng)建以及編輯對(duì)象。我們已經(jīng)覆蓋了Django Rest Framework的主要部分,但是你可以找到該框架更多的特性,通過(guò)查看它的文檔,地址在 http://www.django-rest-framework.org/

總結(jié)

在這章中,你創(chuàng)建了一個(gè)RESTful API給其他的服務(wù)器去與你的web應(yīng)用交互。

一個(gè)額外的章節(jié)第十三章,Going Live需要在線下載:https://www.packtpub.com/sites/default/files/downloads/Django_By_Example_GoingLive.pdf 。第十三章將會(huì)教你如何使用uWSGI以及NGINX去構(gòu)建一個(gè)生產(chǎn)環(huán)境。你還將學(xué)習(xí)到如何去導(dǎo)入一個(gè)定制中間件以及創(chuàng)建定制管理命令。

你已經(jīng)到達(dá)這本書(shū)的結(jié)尾。恭喜你!你已經(jīng)學(xué)習(xí)到了通過(guò)Django構(gòu)建一個(gè)成功的web應(yīng)用所需的技能。這本書(shū)指導(dǎo)你通過(guò)其他的技術(shù)與Django集合去開(kāi)發(fā)了幾個(gè)現(xiàn)實(shí)生活能用到的項(xiàng)目。現(xiàn)在你已經(jīng)準(zhǔn)備好去創(chuàng)建你自己的Django項(xiàng)目了,無(wú)論是一個(gè)簡(jiǎn)單的樣品還是一個(gè)一個(gè)強(qiáng)大的wen應(yīng)用。

祝你下一次Django的冒險(xiǎn)好運(yùn)!

譯者總結(jié)

完結(jié)撒花!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

2016年12月7日開(kāi)始翻譯第一章到今天2017年3月29日終于全書(shū)翻譯完成!

對(duì)我而言真的是一次非常大膽的嘗試,但終于堅(jiān)持下來(lái)了!!!!!!!!

要相信自己做的到!

請(qǐng)?jiān)试S我多打幾個(gè)感嘆號(hào)表達(dá)我的心情!

感謝所有支持和鼓勵(lì)的人!

精校工作也正在不斷的進(jìn)行中,請(qǐng)大家原諒一開(kāi)始的渣翻!

其實(shí)還有很多很多想說(shuō)的話,但是不知道說(shuō)什么好!就這樣吧!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,428評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,024評(píng)論 3 413
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,285評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,548評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,328評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,878評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,971評(píng)論 3 439
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,098評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,616評(píng)論 1 331
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,554評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,725評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,243評(píng)論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,971評(píng)論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,361評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,613評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,339評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,695評(píng)論 2 370

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