1. 程式人生 > >Django 之REST framework學習5:關聯性和超連結API(Relationships & Hyperlinked APIs)

Django 之REST framework學習5:關聯性和超連結API(Relationships & Hyperlinked APIs)

當前我們API的內部關聯性都是通過主鍵來代表的,接下來我們要通過超連結的方式來提高內聚和可發現性,意思就是提高關聯性!

給我們API的根目錄建立一個endpoint

找到了endpoint原始碼貼一下:

@property
def endpoint(self):
    """The endpoint that matched the request.  This in combination with
    :attr:`view_args` can be used to reconstruct the same or a
    modified URL.  If an exception happened when matching, this will
    be ``None``.
    """
if self.url_rule is not None: return self.url_rule.endpoint

理解下endpoint先:
    實際上這個endpoint就是一個Identifier,每個檢視函式都有一個endpoint,當有請求來到的時候,用它來知道到底使用哪一個檢視函式;現在我們需要在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) })

為高亮的snippets建立endpoint:

不像其他API的endpoint,我們不想用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)

snippets/urls.py中新增:

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

接著新增:

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

超連結化我們的API

處理實體間的關係是Web API設計的難點之一,這裡有很多種方法去表示一種關係(不一一翻譯了):


  • Using primary keys.
  • Using hyperlinking between entities.
  • Using a unique identifying slug field on the related entity.
  • Using the default string representation of the related entity.
  • Nesting the related entity inside the parent representation.
  • Some other custom representation.

REST framework以上形式都支援,而且可以將它們應用於正向或者反向關聯當中,當然也適用於外來鍵這樣的關係:
這次我們在實體間使用超連結的方式,那麼我們要在我們的serializers 中把ModelSerializer換成
HyperlinkedModelSerializer
HyperlinkedModelSerializerModelSerializer有以下區別:

  1. 預設沒有id欄位;
  2. url欄位,要用HyperlinkedIdentityField代表;
  3. 關聯關係用HyperlinkedRelatedField表示,而不是PrimaryKeyRelatedField

snippets/serializers.py中重寫我們的serializers :
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
    owner = serializers.ReadOnlyField(source='owner.username')
    #這裡我們增加了highlight 欄位,型別和url 欄位是一樣的,只是它指向了<code>'snippet-highlight'</code>而不是
    #<code>'snippet-detail'</code>的url pattern
    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')

現在有了'.json'這樣的格式化字尾的URLs,
我們還需要給highlight指定'.html'格式的字尾。

確保我們的URL patterns有自己的名字

如果我們想擁有一個超連結形式的API,我們需要給URL patterns指定名字:
snippets/urls.py中:

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

# API endpoints
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')
])

增加分頁

使用者 and code snippets 可能會返回很多例項,我們就需要對返回的結果進行分頁處理,
稍微改下tutorial/settings.py,我們就能改變結果的預設展示形式:

#REST_FRAMEWORK 可以與專案的其他設定很好的進行區分
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10
}

測試~