Django-REST-framework使用技巧(三)

  • 1. 關(guān)系和超鏈接 API(Relationships & hyperlinked APIs)
  • 2. 視圖集合和路由(ViewSets & Routers)
  • 3. 架構(gòu)和客戶端庫(Schemas & client libraries)

1. 關(guān)系和超級鏈接API

目前我們的API中的關(guān)系的通過主鍵來表示。我們下面將改進(jìn)API的內(nèi)聚力和可現(xiàn)性,而不是使用超鏈接來進(jìn)來進(jìn)行關(guān)系。

為我們的API的根地址創(chuàng)建端點(diǎn)

現(xiàn)在我們有snippetsusers的端點(diǎn),但是我們沒有一個指向我們API的入口。我們需要創(chuàng)建一個,我們將使用基于函數(shù)的常規(guī)視圖和我們前面介紹的@api_view裝飾器。在你的quickstart/view.py中添加:

# quickstart/view.py

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse


@api_view(['GET'])
def api_root(request, format=None):
    return Response({
        'users' : reverse('user-list', request=request, format=format),
        'snippets': reverse('snippet-list', request=request, format=format)
    })

這里應(yīng)該注意兩件事,首先我們使用REST frameworkreverse函數(shù)來返回完全限定的URL;
其次,URL模式通過我們下面的quickstart/urls.py中聲明的便利名稱進(jìn)行表示。

為高亮顯示snippets創(chuàng)建端點(diǎn)

另一個明顯的事情是我們的pastebin API仍然缺少高亮的顯示代碼端點(diǎn)。

與其他API端點(diǎn)不同,我們不想使用JSON,而是只呈現(xiàn)HTML表示。REST framework提供了兩種HTML渲染器,一種是使用模板來處理渲染HTML,另一種渲染器是我們要用于此端點(diǎn)的渲染器。

在創(chuàng)建代碼高亮顯示視圖時候,我們需要考慮的另一件事是:不存在我們可以使用的通用視圖。我們不是返回一個對象實(shí)例,而是返回宇哥對象實(shí)例屬性。

我們將使用基類來表示實(shí)例,并創(chuàng)建我們自己的.get()方法,而不是使用具體的通用視圖。在你的quickstart/view.py中添加:

# quickstart/view.py

from rest_framework import permissions, renderers

class SnippetHighlight(generics.GenericAPIView):
    queryset = Snippet.objects.all()
    renderer_classes = (renderers.StaticHTMLRenderer,)

    def get(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

像往常一樣,我們需要將我們新創(chuàng)建的視圖添加到URLconf中,我們將在quickstart/urls.py中添加一個新的url模式:

url('^$', api_root),

然后為高亮代碼snippet添加一個url模式:

 url('^snippets/(?P<pk>[0-9]+)/highlight/$',SnippetHighlight.as_view()),

為我們API加上超鏈接

處理實(shí)體之間的關(guān)系是我們Web API設(shè)計中更具挑戰(zhàn)性的方面之一,這里有一些我們選擇代表關(guān)系的不同方法:

  • 使用主鍵
  • 在實(shí)體之間使用超鏈接
  • 在相關(guān)實(shí)體上使用唯一的標(biāo)識字段
  • 使用默認(rèn)的字符串代表相關(guān)實(shí)體
  • 將相關(guān)的實(shí)體嵌套在父代表中
  • 一切其他自定義的表示

REST framework支持這些樣式,并且可在正式或者反向關(guān)系中應(yīng)用他們?;蛘咄ㄟ^自定義管理器(如通用外鍵)應(yīng)用他們。

在這種情況下,我們希望在實(shí)體之間使用超鏈接樣式,為了做到這一點(diǎn),我們將修改我們的序列化器來擴(kuò)展HyperlinkedModelSerializer,而不是現(xiàn)有的ModelSerializer。

HyperLinkedModelSerializerModelSerializer有一下區(qū)別:

  • 它不包含id字段
  • 它包含一個url字段,使用HyperlinkedIdentityField .
  • 關(guān)系使用HyperlinkedRelatedField,而不是PrimaryKeyRelatedField。

我們可以輕松的重寫現(xiàn)有的序列化器來使用超鏈接:

# quickstart/serialzers.py


from rest_framework import serializers
from .models import Snippet
from django.contrib.auth.models import User

class SnippetSerializer(serializers.HyperlinkedModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')

    class Meta:
        model = Snippet
        fields = ('url', 'id', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style')


class UserSerializer(serializers.HyperlinkedModelSerializer):
    snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail',read_only=True)

    class Meta:
        model = User
        fields = ('url', 'id', 'username', 'snippets')

注意,我們還新添加了一個新的highlight字段。該字段的url字段類型相同,不同之處在于它指向的是snippet-highlighturl模式,而不是snippet-detailurl模式。

因?yàn)槲覀円呀?jīng)包含了格式后綴的Url。例如.json,我們還需要在highlight字段上指出,任何格式后綴的超鏈接返回都應(yīng)該使用.html后綴。

確保我們的URL模式被命名

如果我們要有超鏈接的API,我們需要確保命名URL模式。我們看看我們需要命名的URL模式。

  • 我們的API根地址是指user-listsnippet-list。
  • 我們的snippet序列化器包含一個指向snippet-highlight的字段。
  • 我們的user序列化器包含一個指向'snippet-list'的字段
  • 我們的snippet和user序列化器包含url字段,默認(rèn)情況下將指向{model_name}-detail,在這個例子中就是snippet-detailuser-detail

我們將所有這些名稱添加到我們的URLconf后,我們最后在quickstart/urls.py文件應(yīng)該如下:

quickstart/urls.py

from django.conf.urls import url
from rest_framework.urlpatterns import format_suffix_patterns
from .views import SnippetList, SnippetDetail, UserList, UserDetail, api_root,SnippetHighlight
from django.conf.urls import include

urlpatterns = format_suffix_patterns([
    url('^api-auth/', include('rest_framework.urls')),
    url('^$',api_root),
    url('snippets/$',SnippetList.as_view(), name='snippet-list'),
    url('^snippets/(?P<pk>[0-9]+)/$', SnippetDetail.as_view(), name='snippet-detail'),
    url('^snippets/(?P<pk>[0-9])/highlight/$', SnippetHighlight.as_view(), name='snippet-highlight'),
    url('^users/$', UserList.as_view(), name='user-list'),
    url('^users/(?P<pk>[0-9]+)/$',view=UserDetail.as_view(), name='user-detail'),
])

添加分頁

users和snippets的列表視圖最終會返回很多實(shí)例,所以我們真的要確保結(jié)果進(jìn)行分頁。并允許API客戶端遍歷每個單獨(dú)頁面。
我們可以通過稍微修改tutorial/setting.py,文件來更改默認(rèn)列表樣式可以使用分頁。添加一下設(shè)置:

# tutorial/setting.py

REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10
}

注意,REST framework中的所有設(shè)置都放在一個名為REST——FRMEWORK的字典中,這有助于他們與其他項(xiàng)目保持良好的分離。

如果需要,我們也可以自定義分頁的樣式,但是這里,我們會一直使用默認(rèn)。

瀏覽API

啟動服務(wù)·

python manage.py runserver

在瀏覽器輸入http://127.0.0.1:8000/quickstart/snippets/,結(jié)果如下圖所示:

hyper.png

你還可以在 snippet 實(shí)例上看到 “highlight” 鏈接,這會帶您跳轉(zhuǎn)到代碼高亮顯示的 HTML 頁面。
還有url的鏈接

2.視圖集合和路由(ViewSets & Routers)

REST framework 包括一個用于處理ViewSets的抽象,它允許開發(fā)人員集中精力對API的狀態(tài)和交互進(jìn)行建模,并保留URL結(jié)構(gòu),根據(jù)通用的約定自動處理。

ViewSet類與View類幾乎相同。只是他們提供諸如readupdate操作。而不提供諸如GET或則PUT等方法處理程序。

一個ViewSet類最后時刻只能綁定一組方法處理程序,當(dāng)它被實(shí)例化為一組視圖的時候,通常通過使用一個Router類來處理定義復(fù)雜的URL。

使用ViewSet重構(gòu)

我們來看看當(dāng)前的一組視圖,并將它們重構(gòu)為視圖集。
首先讓我們將UserListUserDetail視圖重構(gòu)為單個UserViewSet。我們可以刪除這兩個視圖,并用一個類替換它們:

# quickstart/view.py

from rest_framework import viewsets #新增導(dǎo)入

class UserViewSet(viewsets.ReadOnlyModelViewSet):
    # 這個視圖集自動提供 list 和 detail 操作

    queryset = User.objects.all()
    serializer_class = UserSerializer

這里我們使用了ReadOnlyModelViewSet類來自動提供默認(rèn)的”只讀“操作。我們?nèi)匀幌裎覀兪褂贸R?guī)駛?cè)霑r一樣設(shè)置querysetserializer_class屬性,但是我們不需要為兩個單獨(dú)的類,提供相同的信息。

接下來我們將替換SnippetList,SnippetDetailSnippetHihtlight視圖類。我們可以刪除這三個視圖,并再次使用一個類去替換它們。

# quickstart/view.py

class SnippetViewSet(viewsets.ModelViewSet):

    """
    這個視圖集自動提供 'list' 'create' 'retrieve' 'update' 和 'destroy'
    另外我們還提供一個額外的'highlight'操作
    """
    queryset = Snippet.objects.all()
    serializer_class = SnippetSerializer
    permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,)

    @action(detail=True, renderers_classed = [renderers.StaticHTMLRenderer])
    def highlight(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)
    
    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

這次我們使用了ModelViewSet類來獲得完整的默認(rèn)讀寫操作集。

注意,我們還使用了@action裝飾器來創(chuàng)建一個名為highlight的自定義操作。這個裝飾器可以用來添加任何不符合標(biāo)準(zhǔn)create/update/delete樣式的自定義端點(diǎn)。

使用@action裝飾器的自定義操作默認(rèn)會響應(yīng)GET請求。如果我們想要響應(yīng)POST請求的操作,我們可以使用methods參數(shù)。

自定義操作的URL默認(rèn)取決于方法名稱本身。如果需要更改URL的構(gòu)造方式,可以包含url_path作為裝飾器的關(guān)鍵字參數(shù)。

明確地將ViewSet綁定到URL

當(dāng)我們定義URLConf時,處理程序方法只能綁定到操作上。為了看看到底發(fā)生了什么,讓我們首先從我們的ViewSets中明確地創(chuàng)建一組視圖。

quickstart/urls.py文件中,我們將ViewSet類綁定到一組具體視圖中。

# quickstart/urls.py

from .views import api_root, SnippetViewSet, UserViewSet
from rest_framework import renderers

snippet_list = SnippetViewSet.as_view({
    'get': 'list',
    'post': 'create',
})

snippet_detail = SnippetViewSet.as_view({
    'get': 'retrieve',
    'put': 'update',
    'patch': 'partial_update',
    'delete': 'destroy',
})

snippet_highlight = SnippetViewSet.as_view({
    'get': 'highlight'
}, renderers_class=[renderers.StaticHTMLRenderer])

user_list = UserViewSet.as_view({
    'get': 'list'
})

user_detail = UserViewSet.as_view({
    'get': 'retrieve'
})

注意我們是如何從每個ViewSet類創(chuàng)建多個視圖,通過將HTTP方法綁定到每個視圖所需的操作中。

現(xiàn)在我們已經(jīng)將資源綁定到具體的視圖中,我們可以像往常一樣在URL conf中注冊視圖。

urlpatterns = format_suffix_patterns([
    url('^api-auth/', include('rest_framework.urls')),
    url('^$',api_root),
    url('snippets/$', snippet_list, name='snippet-list'),
    url('^snippets/(?P<pk>[0-9]+)/$', snippet_detail, name='snippet-detail'),
    url('^snippets/(?P<pk>[0-9])/highlight/$', snippet_highlight, name='snippet-highlight'),
    url('^users/$', user_list, name='user-list'),
    url('^users/(?P<pk>[0-9]+)/$', user_detail, name='user-detail'),
])

使用Routers

因?yàn)槲覀兪褂?code>ViewSet類而不是View類,所以實(shí)際上我們不需要自己設(shè)計URL。將資源鏈接到視圖和URL的約定可以使用Router類自動處理,我們需要做的就使用路由器注冊相應(yīng)的視圖集,然后讓他執(zhí)行其余操作。

這里我們重寫quickstart/urls.py文件:

# quickstart/urls.py

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

# 創(chuàng)建路由器并注冊我們的視圖。
router = DefaultRouter()
router.register('snippets', views.SnippetViewSet)
router.register('users', views.UserViewSet)

# API url 現(xiàn)在有路由器自動確定
urlpatterns = [
    url('^', include(router.urls))
]

用路由器注冊視圖集類似于提供urlpattern.我們包括兩個參數(shù)--視圖的URL前綴和視圖集本身。

我們使用DefaultRouter類為我們自動創(chuàng)建了API根視圖,所以我們現(xiàn)在可以從views模塊中刪除api_root方法。

視圖與視圖集之間的權(quán)衡

使用視圖集可以是一個常用的抽象。它有助于確保URL約定在你的API中保存一致,最大限度地減少編寫所需的代碼量,并允許你專注于API提供的交互,而不是URL conf的細(xì)節(jié)。

這并不意味著它總是正確的做法。但是用基于類的視圖而不是基于函數(shù)的視圖的時候,也有類似的權(quán)衡考慮。使用視圖集不像單獨(dú)構(gòu)建視圖那樣明確。

3.概要和客戶端庫(Schemas & client libraries)

概要是一種機(jī)器可閱讀文檔,用于描述可用API路徑,器URLS以及他們支持的操作。
高腰可以是自動生成文檔的有用工具,也可以是用于驅(qū)動,可以與API進(jìn)行交互的動態(tài)客戶端庫。

Core API

為了提供概要支持REST框架使用Core API.

CoreAPI是用于描述API的文檔規(guī)范,它用于提供可用路徑的內(nèi)部表示形式和API公開的可能的交互。他可以用于服務(wù)器端或者客戶端。

當(dāng)使用服務(wù)端時候,Core API 允許支持API支持呈現(xiàn)范圍廣泛的概要或者超媒體格式。

當(dāng)使用客戶端時,核心API允許動態(tài)驅(qū)動的客戶端庫,它可以與任何公開受支持的概要或者超媒體格式的API交互。

添加概要

REST框架支持明確定義的概要視圖或者自動生成概要。由于我們使用的是視圖集和路由,我們可以簡單地使用自動概要生成。
你需要安裝coreapi,python包才能包含API概要。

pip install coreapi

現(xiàn)在我們可以通過在url配置中包含一個主動生成的概要視圖來為API添加概要。

# quickstart/urls.py

from rest_framework.schemas import get_schema_view

urlpatterns = [
    url('^schema/$', schema_view), #新增
    url('^', include(router.urls))
]

如果你在瀏覽器中訪問API的根路徑,那么你現(xiàn)在應(yīng)該就可以看到core json表示形式是另一個可用選項(xiàng)。

coreapi.png

我們也可以通過在Accept標(biāo)識頭中指定所需的內(nèi)容類型從從命令行請求概要。

$ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json
HTTP/1.0 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/coreapi+json

{
    "_meta": {
        "title": "Pastebin API"
    },
    "_type": "document",
    ...

默認(rèn)輸入樣式是使用Core JSON編碼。
還支持其他概要格式,如Open API(以前叫Swagger)。

使用命令行客戶端

現(xiàn)在我們的API暴露了一個概要路徑,我們可以使用一個動態(tài)的客戶端庫與API進(jìn)行交互,為了演示這個,我們使用CoreAPI命令行客戶端。

命令行客戶端作為一個coreapi-cli包提供:

pip install coreapi-cli

現(xiàn)在檢查他在命令行上是否可用...


client.png

首先我們使用命令行客戶端加載API概要。

$ coreapi get http://127.0.0.1:8000/schema/
<Pastebin API "http://127.0.0.1:8000/schema/">
    snippets: {
        highlight(id)
        list()
        read(id)
    }
    users: {
        list()
        read(id)
    }

我們還沒有認(rèn)證,所以我們現(xiàn)在只能看到只讀路徑,這與我們設(shè)置的API權(quán)限是一致的。
我們使用命令行客戶端,嘗試列出現(xiàn)有代碼片段:

$ coreapi action snippets list
[
    {
        "url": "http://127.0.0.1:8000/snippets/1/",
        "id": 1,
        "highlight": "http://127.0.0.1:8000/snippets/1/highlight/",
        "owner": "lucy",
        "title": "Example",
        "code": "print('hello, world!')",
        "linenos": true,
        "language": "python",
        "style": "friendly"
    },
    ...

一些API路徑需要命名參數(shù)。例如,要獲取特定代碼片段的高亮HTML表示,我們需要提供一個id。

$ coreapi action snippets highlight --param id=1
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html>
<head>
  <title>Example</title>
  ...
  

驗(yàn)證我們的客戶端

如果我們想要創(chuàng)建,編輯,和刪除代碼片段,我們需要進(jìn)行有效的用戶身份驗(yàn)證,在這種情況下,我們只需要使用基本的auth。
請確保使用實(shí)際的用戶名和密碼替換下面的<username><password>.

$ coreapi credentials add 127.0.0.1 <username>:<password> --auth basic
Added credentials
127.0.0.1 "Basic <...>"

現(xiàn)在,如果我們再次提取概要,我么應(yīng)該可以看到一組可用的交互。

$ coreapi reload
Pastebin API "http://127.0.0.1:8000/schema/">
    snippets: {
        create(code, [title], [linenos], [language], [style])
        delete(id)
        highlight(id)
        list()
        partial_update(id, [title], [code], [linenos], [language], [style])
        read(id)
        update(id, code, [title], [linenos], [language], [style])
    }
    users: {
        list()
        read(id)
    }

我們現(xiàn)在能夠與這些路徑行交互。例如,要創(chuàng)建一個新的代碼片段:

$ coreapi action snippets create --param title="Example" --param code="print('hello, world')"
{
    "url": "http://127.0.0.1:8000/snippets/7/",
    "id": 7,
    "highlight": "http://127.0.0.1:8000/snippets/7/highlight/",
    "owner": "lucy",
    "title": "Example",
    "code": "print('hello, world')",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}

然后刪除一個代碼片段:

$ coreapi action snippets delete --param id=7

除了命令行客戶端,開發(fā)人員還可以使用客戶端庫與你的API進(jìn)行交互。Python客戶端庫是第一個可用的庫,并且計劃即將發(fā)布一個Javascript客戶端庫。

有關(guān)定制模式生成和使用Core API客戶端庫的更多詳細(xì)信息,您需要參考完整的文檔。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容