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

1. 關係和超級連接API

目前咱們的API中的關係的經過主鍵來表示。咱們下面將改進API的內聚力和可現性,而不是使用超連接來進來進行關係。html

爲咱們的API的根地址建立端點

如今咱們有snippetsusers的端點,可是咱們沒有一個指向咱們API的入口。咱們須要建立一個,咱們將使用基於函數的常規視圖和咱們前面介紹的@api_view裝飾器。在你的quickstart/view.py中添加:python

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

這裏應該注意兩件事,首先咱們使用REST frameworkreverse函數來返回徹底限定的URL;
其次,URL模式經過咱們下面的quickstart/urls.py中聲明的便利名稱進行表示。django

爲高亮顯示snippets建立端點

另外一個明顯的事情是咱們的pastebin API仍然缺乏高亮的顯示代碼端點。json

與其餘API端點不一樣,咱們不想使用JSON,而是隻呈現HTML表示。REST framework提供了兩種HTML渲染器,一種是使用模板來處理渲染HTML,另外一種渲染器是咱們要用於此端點的渲染器。api

在建立代碼高亮顯示視圖時候,咱們須要考慮的另外一件事是:不存在咱們可使用的通用視圖。咱們不是返回一個對象實例,而是返回宇哥對象實例屬性。瀏覽器

咱們將使用基類來表示實例,並建立咱們本身的.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)

像往常同樣,咱們須要將咱們新建立的視圖添加到URLconf中,咱們將在quickstart/urls.py中添加一個新的url模式:架構

url('^$', api_root),

而後爲高亮代碼snippet添加一個url模式:app

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

爲咱們API加上超連接

處理實體之間的關係是咱們Web API設計中更具挑戰性的方面之一,這裏有一些咱們選擇表明關係的不一樣方法:框架

  • 使用主鍵
  • 在實體之間使用超連接
  • 在相關實體上使用惟一的標識字段
  • 使用默認的字符串表明相關實體
  • 將相關的實體嵌套在父表明中
  • 一切其餘自定義的表示

REST framework支持這些樣式,而且可在正式或者反向關係中應用他們。或者經過自定義管理器(如通用外鍵)應用他們。

在這種狀況下,咱們但願在實體之間使用超連接樣式,爲了作到這一點,咱們將修改咱們的序列化器來擴展HyperlinkedModelSerializer,而不是現有的ModelSerializer

HyperLinkedModelSerializerModelSerializer有一下區別:

  • 它不包含id字段
  • 它包含一個url字段,使用HyperlinkedIdentityField .
  • 關係使用HyperlinkedRelatedField,而不是PrimaryKeyRelatedField

咱們能夠輕鬆的重寫現有的序列化器來使用超連接:

# 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模式。

由於咱們已經包含了格式後綴的Url。例如.json,咱們還須要在highlight字段上指出,任何格式後綴的超連接返回都應該使用.html後綴。

確保咱們的URL模式被命名

若是咱們要有超連接的API,咱們須要確保命名URL模式。咱們看看咱們須要命名的URL模式。

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

咱們將全部這些名稱添加到咱們的URLconf後,咱們最後在quickstart/urls.py文件應該以下:

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的列表視圖最終會返回不少實例,因此咱們真的要確保結果進行分頁。並容許API客戶端遍歷每一個單獨頁面。
咱們能夠經過稍微修改tutorial/setting.py,文件來更改默認列表樣式可使用分頁。添加一下設置:

# tutorial/setting.py

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

注意,REST framework中的全部設置都放在一個名爲REST——FRMEWORK的字典中,這有助於他們與其餘項目保持良好的分離。

若是須要,咱們也能夠自定義分頁的樣式,可是這裏,咱們會一直使用默認。

瀏覽API

啓動服務·

python manage.py runserver

在瀏覽器輸入http://127.0.0.1:8000/quickstart/snippets/,結果以下圖所示:

在這裏插入圖片描述

你還能夠在 snippet 實例上看到 「highlight」 連接,這會帶您跳轉到代碼高亮顯示的 HTML 頁面。
還有url的連接

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

REST framework 包括一個用於處理ViewSets的抽象,它容許開發人員集中精力對API的狀態和交互進行建模,並保留URL結構,根據通用的約定自動處理。

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

一個ViewSet類最後時刻只能綁定一組方法處理程序,當它被實例化爲一組視圖的時候,一般經過使用一個Router類來處理定義複雜的URL。

使用ViewSet重構

咱們來看看當前的一組視圖,並將它們重構爲視圖集。
首先讓咱們將UserListUserDetail視圖重構爲單個UserViewSet。咱們能夠刪除這兩個視圖,並用一個類替換它們:

# quickstart/view.py

from rest_framework import viewsets #新增導入

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

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

這裏咱們使用了ReadOnlyModelViewSet類來自動提供默認的」只讀「操做。咱們仍然像咱們使用常規駛入時同樣設置querysetserializer_class屬性,可是咱們不須要爲兩個單獨的類,提供相同的信息。

接下來咱們將替換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類來得到完整的默認讀寫操做集。

注意,咱們還使用了@action裝飾器來建立一個名爲highlight的自定義操做。這個裝飾器能夠用來添加任何不符合標準create/update/delete樣式的自定義端點。

使用@action裝飾器的自定義操做默認會響應GET請求。若是咱們想要響應POST請求的操做,咱們可使用methods參數。

自定義操做的URL默認取決於方法名稱自己。若是須要更改URL的構造方式,能夠包含url_path做爲裝飾器的關鍵字參數。

明確地將ViewSet綁定到URL

當咱們定義URLConf時,處理程序方法只能綁定到操做上。爲了看看到底發生了什麼,讓咱們首先從咱們的ViewSets中明確地建立一組視圖。

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類建立多個視圖,經過將HTTP方法綁定到每一個視圖所需的操做中。

如今咱們已經將資源綁定到具體的視圖中,咱們能夠像往常同樣在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

由於咱們使用ViewSet類而不是View類,因此實際上咱們不須要本身設計URL。將資源連接到視圖和URL的約定可使用Router類自動處理,咱們須要作的就使用路由器註冊相應的視圖集,而後讓他執行其他操做。

這裏咱們重寫quickstart/urls.py文件:

# quickstart/urls.py

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

# 建立路由器並註冊咱們的視圖。
router = DefaultRouter()
router.register('snippets', views.SnippetViewSet)
router.register('users', views.UserViewSet)

# API url 如今有路由器自動肯定
urlpatterns = [
    url('^', include(router.urls))
]

用路由器註冊視圖集相似於提供urlpattern.咱們包括兩個參數--視圖的URL前綴和視圖集自己。

咱們使用DefaultRouter類爲咱們自動建立了API根視圖,因此咱們如今能夠從views模塊中刪除api_root方法。

視圖與視圖集之間的權衡

使用視圖集能夠是一個經常使用的抽象。它有助於確保URL約定在你的API中保存一致,最大限度地減小編寫所需的代碼量,並容許你專一於API提供的交互,而不是URL conf的細節。

這並不意味着它老是正確的作法。可是用基於類的視圖而不是基於函數的視圖的時候,也有相似的權衡考慮。使用視圖集不像單獨構建視圖那樣明確。

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

概要是一種機器可閱讀文檔,用於描述可用API路徑,器URLS以及他們支持的操做。
高腰能夠是自動生成文檔的有用工具,也能夠是用於驅動,能夠與API進行交互的動態客戶端庫。

Core API

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

CoreAPI是用於描述API的文檔規範,它用於提供可用路徑的內部表示形式和API公開的可能的交互。他能夠用於服務器端或者客戶端。

當使用服務端時候,Core API 容許支持API支持呈現範圍普遍的概要或者超媒體格式。

當使用客戶端時,核心API容許動態驅動的客戶端庫,它能夠與任何公開受支持的概要或者超媒體格式的API交互。

添加概要

REST框架支持明肯定義的概要視圖或者自動生成概要。因爲咱們使用的是視圖集和路由,咱們能夠簡單地使用自動概要生成。
你須要安裝coreapi,python包才能包含API概要。

pip install coreapi

如今咱們能夠經過在url配置中包含一個主動生成的概要視圖來爲API添加概要。

# quickstart/urls.py

from rest_framework.schemas import get_schema_view

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

若是你在瀏覽器中訪問API的根路徑,那麼你如今應該就能夠看到core json表示形式是另外一個可用選項。
在這裏插入圖片描述

咱們也能夠經過在Accept標識頭中指定所需的內容類型從從命令行請求概要。

$ 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",
    ...

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

使用命令行客戶端

如今咱們的API暴露了一個概要路徑,咱們可使用一個動態的客戶端庫與API進行交互,爲了演示這個,咱們使用CoreAPI命令行客戶端。

命令行客戶端做爲一個coreapi-cli包提供:

pip install coreapi-cli

如今檢查他在命令行上是否可用...
在這裏插入圖片描述

首先咱們使用命令行客戶端加載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)
    }

咱們尚未認證,因此咱們如今只能看到只讀路徑,這與咱們設置的API權限是一致的。
咱們使用命令行客戶端,嘗試列出現有代碼片斷:

$ 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路徑須要命名參數。例如,要獲取特定代碼片斷的高亮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>
  ...

驗證咱們的客戶端

若是咱們想要建立,編輯,和刪除代碼片斷,咱們須要進行有效的用戶身份驗證,在這種狀況下,咱們只須要使用基本的auth。
請確保使用實際的用戶名和密碼替換下面的<username><password>.

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

如今,若是咱們再次提取概要,我麼應該能夠看到一組可用的交互。

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

咱們如今可以與這些路徑行交互。例如,要建立一個新的代碼片斷:

$ 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

除了命令行客戶端,開發人員還可使用客戶端庫與你的API進行交互。Python客戶端庫是第一個可用的庫,而且計劃即將發佈一個Javascript客戶端庫。

有關定製模式生成和使用Core API客戶端庫的更多詳細信息,您須要參考完整的文檔。

相關文章
相關標籤/搜索