当前,我们的API之间的关系还是通过主键相联系的。在本文,我们将通过使用超链接让我们的API之间的联系更加紧密,更加方便我们使用。
为API创建一个endpoint
现在我们的snippets和users已经有了endpoint,但是我们的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) })
其中,两点需要注意:一是为了返回全长的URL信息,我们使用了REST framework的reverse方法;二是URL的patterns可以很方便的根据名称来区分,这个名称我们后面会在snippets/urls.py中声明。
为高亮的代码片段创建一个endpoint
另外一个比较明显的事情是我们的代码高亮的endpoint还没有实现。
不像其它的API endpoints,我们不想使用JSON,而是使用HTML格式。REST framework提供了两种风格的HTML渲染方式。一个是使用模板templates渲染,另一个是使用预先渲染的HTML(Pre-rendered HTML)。在此,我们就使用第二个。
在创建代码高亮视图的时候,还有一点我们需要考虑到,我们没有现成可用的通用视图。我们返回的不是一个对象实例,而是一个对象实例的属性。
在此,我们就不适用通用视图,我们使用积累GenericAPIView来呈现实例,并自定义.get()方法。
在snippets/views.py文件中添加:
from rest_framework import renderers from rest_framework.response import Response class SnippetHighlight(generic.GenericAPIView): queryset = Snippet.objects.all() renderer_classes = [renderer.StaticHTMLRenderer] def get(self, request, *args, **kwargs): snippet = self.get_object() return Response(snippet.highlighted)
同样,我们需要在我们的URLconf中添加这个新视图。我们给API root添加一个urlpattern。
在snippets/urls.py文件中添加:
path('', views.api_root),
然后为snippet高亮添加一个url pattern
path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view()),
为API添加超链接
处理各个项之间的关系是我们API设计中一个很有挑战性的方面。为各个项之间添加关系,我们有很多种方法可以选择:
使用主键;
使用超链接
在关联的项中使用一个特定的可以确定身份的slug field;
使用关联的项的默认的字符串表达形式;
将关联项嵌套在parent representation中;
其它的一些用户自定义的表现方法。
REST framework支持所有这些方法。
我们在此就使用超链接形式。
为此,我们需要修改一下serializers,继承自HyperlinkedModelSerializer而不是ModelSerializer
HyperlinkedModelSerializer和ModelSerializer有一下几点不同:
前者默认不包含id域;
前者引入一个url域;
关系使用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')
为URL pattern命名
为了得到超链接的API,我们需要保证给每一个URL pattern命名,我们看一看如何给它们命名。
API的根指向'user-list'和'snippet-list'
snippet序列化器中包括一个域指向'snippet-highlight',
user的序列化器包含一个域指向'snippet-field'
snippet和user序列化器中有一个'url'域默认指向'{model_name}-detail',即'snippet-detail'和'user-detail'
将这些名字添加到我们的snippets/urls文件中后,我们的的文件如下:
from django.urls import path from rest_framework.urlpatterns import format_suffix_patterns from .views import SnippetList, SnippetDetail, UserList, UserDetail, api_root, SnippetHighlight urlpatterns = [ path('', api_root), path('snippets/', SnippetList.as_view(), name='snippet-list'), path('snippet/<int:pk>/', SnippetDetail.as_view(), name='snippet-detail'), path('users/', UserList.as_view(), name='user-list'), path('user/<int:pk>/', UserDetail.as_view(), name='user-detail'), path('snippets/<int:pk>/highlight/', SnippetHighlight.as_view(), name='snippet-highlight'), ] urlpatterns = format_suffix_patterns(urlpatterns)
分页
我们的用户和代码片段页面可能会被服务器返回得到很多的实例信息,因此我们需要对结果进行翻页,并且允许API在各个不同的页面之间跳转。
我们可以直接修改项目文件夹下的settings.py文件,添加如下内容:
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10}
注意,所有的REST framework相关的配置信息都存储在REST_FRAMEWORK字典下。
我们也可以自定义分页的显示效果。如果没有特殊需要,我们直接使用默认的即可。
浏览器查看
现在我们打开浏览器查看我们的API,现在发现,我们可以直接通过点击API的链接进行跳转了。
另外,我们在snippet实例页面也可以看到有个'highlight'链接,点击之后,我们高亮效果之后的代码片段了。
本文中的源码可以在Github上查看。点我查看本文源码