当前我们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
。
HyperlinkedModelSerializer
和ModelSerializer
有以下区别:
- 默认没有
id
字段;- 有
url
字段,要用HyperlinkedIdentityField
代表;- 关联关系用
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
}