1. 程式人生 > >5 - Relationships and hyperlinked APIs

5 - Relationships and hyperlinked APIs

教程 5:關係和超連結 API

目前我們的 API 中的關係是通過使用主鍵來表示。在本教程的這一部分中,我們將改進 API 的內聚力和可發現性,而不是使用超連結來進行關係。

為我們的 API 的根地址建立端點

現在我們有 ‘snippets’ 和 ‘users’ 端點,但我們沒有一個指向我們 API 的入口。要建立一個,我們將使用基於函式的常規檢視和我們前面介紹的 @api_view 裝飾器。在你的 snippets/views.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 framework 的 reverse 函式來返回完全限定的URL;其次,URL 模式通過我們稍後將在我們的 snippets/urls.py 中宣告的便利名稱進行標識。

為高亮顯示 snippets 建立端點

另一個明顯的事情是我們的 pastebin API 仍然缺少高亮顯示程式碼的端點。

與所有其他 API 端點不同,我們不想使用 JSON,而只是呈現 HTML 表示。REST framework 提供了兩種 HTML 渲染器,一種是使用模板來處理渲染的 HTML,另一種是處理預渲染的 HTML。第二個渲染器是我們要用於此端點的渲染器。

在建立程式碼高亮顯示檢視時,我們需要考慮的另一件事是不存在我們可以使用的具體的通用檢視。我們不是返回一個物件例項,而是返回一個物件例項的屬性。

我們將使用基類來表示例項,並建立我們自己的 .get() 方法,而不是使用具體的通用檢視。在您的 snippets/views.py 中新增:

from rest_framework import renderers
from rest_framework.response import Response

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 中。我們將在 snippets/urls.py 中為我們的新 API 根路徑新增一個 url 模式:

url(r'^$', views.api_root),

然後為高亮 snippet 新增一個 url 模式:

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

超連結我們的 API

處理實體之間的關係是 Web API 設計中更具挑戰性的方面之一。這裡有一些我們可能會選擇代表關係的不同方法:

  • 使用主鍵。
  • 在實體之間使用超連結。
  • 在相關實體上使用唯一的標識欄位。
  • 使用預設字串代表相關實體。
  • 將相關實體巢狀在父代表中。
  • 一些其他自定義表示。

REST framework 支援所有這些樣式,並且可以在正向或反向關係中應用它們,或者通過自定義管理器 (如通用外來鍵) 應用它們。

在這種情況下,我們希望在實體之間使用超連結樣式。為了做到這一點,我們將修改我們的序列化器來擴充套件 HyperlinkedModelSerializer,而不是現有的 ModelSerializer

HyperlinkedModelSerializerModelSerializer 有以下區別:

  • 它預設不包含 id 欄位。
  • 它包含一個 url 欄位,使用 HyperlinkedIdentityField
  • 關係使用 HyperlinkedRelatedField,而不是 PrimaryKeyRelatedField

我們可以輕鬆地重寫現有的序列化器來使用超連結。在你的 snippets/serializers.py 中新增:

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-highlight' url 模式,而不是 'snippet-detail' url 模式。

因為我們已經包含了格式字尾的 URL,例如 '.json',我們還需要在 highlight 欄位上指出任何格式字尾的超連結返回應該使用 .html' 字尾。

確保我們的 URL 模式被命名

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

  • 我們的 API 根地址是指 'user-list''snippet-list'
  • 我們的 snippet 序列化器包含一個指向 'snippet-highlight' 的欄位。
  • 我們的 user 序列化器包含一個指向 'snippet-detail' 的欄位。
  • 我們的 snippet 和 user 序列化器包括 'url' 欄位,預設情況下將指向 '{model_name}-detail',在這個例子中就是 'snippet-detail''user-detail'

將所有這些名稱新增到我們的 URLconf 後,我們最後的 snippets/urls.py 檔案應如下所示:

from django.conf.urls import url, include
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

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

新增分頁

users 和 snippets 的列表檢視最終會返回很多例項,所以我們真的要確保對結果進行分頁,並允許 API 客戶端遍歷每個單獨的頁面。

我們可以通過稍微修改 tutorial/settings.py 檔案來更改預設列表樣式以使用分頁。新增以下設定:

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

請注意,REST framework 中的所有設定都放在一個名為 REST_FRAMEWORK 的字典中,這有助於讓他們與其他專案設定保持良好的分離。

如果需要,我們也可以自定義分頁樣式,但在這個例子中,我們將一直使用預設設定。

瀏覽 API

如果我們開啟瀏覽器並導航到可瀏覽的 API,您會發現現在您可以通過簡單的連結訪問 API。

您還可以在 snippet 例項上看到 “highlight” 連結,這會帶您跳轉到程式碼高亮顯示的 HTML 頁面。

本教程的第 6 部分中,我們將介紹如何使用 ViewSets 和 Routers 來減少構建 API 所需的程式碼量。