Tutorial 5: Relationships & Hyperlinked APIs
到目前為止,在我們的API中關(guān)系(relationship)還是通過(guò)主鍵來(lái)表示的。在這部分的教程中,我們將用超鏈接方式來(lái)表示關(guān)系,從而提升API的統(tǒng)一性和可發(fā)現(xiàn)性。
1. 為API根創(chuàng)建一個(gè)endpoint
到目前為止,我們已經(jīng)有了'snippets'和'users'的endpoint, 但是我們還沒(méi)有為我們的API單獨(dú)創(chuàng)立一個(gè)端點(diǎn)入口。我們可以用常規(guī)的基于函數(shù)的view和之前介紹的 @api_view 修飾符來(lái)創(chuàng)建。
from rest_framework import renderers
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)
})
請(qǐng)注意,我們用 REST framework 的 reverse 函數(shù)來(lái)返回完全合規(guī)的URLs.
2. 為高亮的Snippet創(chuàng)建一個(gè)endpoint
我們目前還沒(méi)有為支持代碼高亮的Snippet創(chuàng)建一個(gè)endpoints.
與之前的API endpoints不同, 我們將直接使用HTML呈現(xiàn),而非JSON。在 REST framework中有兩種風(fēng)格的HTML render, 一種使用模板來(lái)處理HTML,一種則使用預(yù)先處理的方式。在這里我們使用后者。
另一個(gè)需要我們考慮的是,對(duì)于高亮代碼的view并沒(méi)有具體的泛型view可以直接利用。我們將只返回實(shí)例的一個(gè)屬性而不是對(duì)象實(shí)例本身。
沒(méi)有具體泛型view的支持,我們使用基類來(lái)表示實(shí)例,并創(chuàng)建我們自己的 .get() 方法。在你的 snippets.views 中增加:
from rest_framework import renderers
from rest_framework.response import Response
class SnippetHighlight(generics.SingleObjectAPIView):
model = Snippet
renderer_classes = (renderers.StaticHTMLRenderer,)
def get(self, request, *args, **kwargs):
snippet = self.get_object()
return Response(snippet.highlighted)
和以往一樣,我們需要為新的view增加新的URLconf,如下增加urlpatterns:
url(r'^$','api_root'),
還需要為代碼高亮增加一個(gè)urlpatterns:
url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', views.SnippetHighlight.as_view()),
3. API超鏈接化
在Web API設(shè)計(jì)中,處理實(shí)體間關(guān)系是一個(gè)有挑戰(zhàn)性的工作。我們有許多方式來(lái)表示關(guān)系:
- 使用主鍵;
- 使用超鏈接;
- 使用相關(guān)實(shí)體唯一標(biāo)識(shí)的字段;
- 使用相關(guān)實(shí)體的默認(rèn)字符串表示;
- 在父級(jí)表示中嵌入子級(jí)實(shí)體;
- 其他自定義的表示。
REST framework支持所有這些方式,包括正向或者反向的關(guān)系,或者將其應(yīng)用到自定義的管理類中,例如泛型外鍵。
在這部分,我們使用超鏈接方式。為了做到這一點(diǎn),我們需要在序列化器中用 HyperlinkedModelSerializer 來(lái)替代之前的 ModelSerializer.
HyperlinkedModelSerializer 與 ModelSerializer 有如下的區(qū)別:
缺省狀態(tài)下不包含
pk字段;具有一個(gè)
url字段,即HyperlinkedIdentityField類型.用
HyperlinkedRelatedField表示關(guān)系,而非PrimaryKeyRelatedField.-
我們可以很方便的改寫現(xiàn)有代碼來(lái)使用超連接方式:
class SnippetSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.Field(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')class Meta: model = models.Snippet fields = ('url', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style')class UserSerializer(serializers.HyperlinkedModelSerializer):
snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail')class Meta: model = User fields = ('url', 'username', 'snippets')
注意:我們也增加了一個(gè)新的 'highlight' 字段。該字段與 url 字段相同類型。不過(guò)它指向了 'snippet-highlight'的 url pattern, 而非'snippet-detail' 的url pattern.
因?yàn)槲覀円呀?jīng)有一個(gè) '.json'的后綴,為了更好的表明highlight字段鏈接的區(qū)別,使用一個(gè) '.html' 的后綴。
4. 正確使用URL patterns
如果要使用超鏈接API,就必須確保正確的命名和使用 URL patterns. 我們來(lái)看看我們需要命名的 URL patterns:
- 指向
'user-list'和'snippet-list'的API根. - snippet的序列化器,包括一個(gè)
'snippet-highlight'字段. - user序列化器,包含一個(gè)
'snippet-detail'字段. - snippet 和user的序列化器,包含
'url'字段(會(huì)缺省指向'snippet-detail'和'user-detail'.
一番工作之后,最終的 'urls.py' 文件應(yīng)該如下所示:
# API endpoints
urlpatterns = format_suffix_patterns(patterns('snippets.views',
url(r'^$', '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')
))
# Login and logout views for the browsable API
urlpatterns += patterns('',
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
)
5. 添加分頁(yè)
列表view有時(shí)會(huì)返回大量的實(shí)例結(jié)果,所以我們應(yīng)該把結(jié)果分頁(yè)顯示,以便用戶使用。
通過(guò)在 settings.py 中添加如下配置,我們就能在結(jié)果列表中增加分頁(yè)的功能:
REST_FRAMEWORK ={'PAGINATE_BY':10}
請(qǐng)注意REST framework的所有配置信息都是存放在一個(gè)叫做 'REST_FRAMEWORK'的dictionary中,以便于其他配置區(qū)分。
如有必要,你也可以自定義分頁(yè)的方式,這里不再贅述。
6. Browsing the API
If we open a browser and navigate to the browsable API, you'll find that you can now work your way around the API simply by following links.
You'll also be able to see the 'highlight' links on the snippet instances, that will take you to the highlighted code HTML representations.
In part 6 of the tutorial we'll look at how we can use ViewSets and Routers to reduce the amount of code we need to build our API.