Django REST framework 第五章 Relationships & Hyperlinked APIs
到目前为止,API内部的关系是使用主键来代表的。在这篇教程中,我们将提高API的凝聚力和可发现性,通过在相互关系上使用超链接。
Creating an endpoint for the root of our API
现在,我们已经有了snippets和users的终端,但是没有一个单独的终端指向我们的API。使用之前常规的FBV方式和@api_view装饰器创建一个。在你的app的views文件内
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方法来返回完全合格的URLs;其次URL模式由稍后在app的urls文件内申明的便利的名称标识。
Creating an endppoint for the highlighted snippets
另一个明显的问题是在我们的API中仍然缺失代码突出的端点。
不像之前写过的API端点,我们不想使用JSON,而是用网页的表现形式来替代。REST framework提供了2种风格的HTML转换,一种用于处理使用模板呈现的HTML,另一个用于处理预先呈现的HTML。第二种渲染器是我们愿意在这个端点使用的那个。
另外还有一件事需要考虑, 当创建代码高亮视图时,没有我们可以使用的集成通用的视图。我们不会返回一个对象实例,取而代之的是一个对象实例的属性。
展示实例我们将可以使用基础类代替集成通用的视图,然后创建get方法。在app的views文件中添加:
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)
跟之前一样,这里还需要添加新的URL跟视图绑定。为新的API根添加url模式在app内的urls文件中:
path('', views.api_root),
然后添加新的url模式:
path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view()),
Hyperlinking our API
处理实体之间的关系是Web API设计中更具挑战性的方面之一。有很多不同的方式可供我们选择来表示这一关系。
a、使用主键
b、在实体间使用超链接
c、在相关实体上使用唯一的标识字段
d、使用相关实体的默认字符串表示形式
e、将相关实体嵌套在父表示中
f、一些其他的自定制的表示形式
REST framework 支持所有这些方式,并且可以将它们应用于正向反向关系,或者自定制的管理(比如通用的外键)
在这个事例中,我们更倾向于在实体间用超链接方式。为了能够实现,我们将要修改serializers来扩展HyperlinkedModelSerializer代替现有的ModelSerializer:
1、默认不包含id字段
2、它包含一个url字段,使用HyperlinkedIdentityField
3、关系用PrimaryKeyRelatedField代替HyperlinkedRelatedField
用超链接可以很简单的重写已经存在的serializers,在app内的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'代替'snippet-detail'url模式。因为我们包含了格式化后缀的rul比如json,所以也需要在highlight字段上指出,它返回的任何格式化后缀的超链接都应该使用.html后缀。
Making sure our URL patterns are named
如果我们想要用超链接的API,需要确保ur已经命名。一起看一下那些url需要命名
a、引用'user-list'和'snippet-list'的根API
b、包含在snippet serializer中的一个字段引用'snippet-highlight'
c、包含在user serializer中的一个字段引用'snippet-detail'
d、默认的snippet和user serializers中包含url字段的,将引用'{model_name}-detail',在这个事例里将会是'snippet-detail'和
'user-detail'
将这些名称全部加入url配置后,最终app 内的urls文件应该是这样的:
from app01 import views from django.urls import path, include from rest_framework.urlpatterns import format_suffix_patterns app_name = 'app01' urlpatterns = [ path('snippets/', views.SnippetList.as_view(), name='snippet-list'), path('snippets/<int:pk>/', views.SnippetDetail.as_view(), name='snippet-detail'), path('users/', views.UserList.as_view(), name='user-list'), path('users/<int:pk>/', views.UserDetail.as_view(), name='user-detail'), path('snippets/<int:pk>/highlight/', views.SnippetHighlight.as_view(), name='snippet-highlight'), path('', views.api_root), ] urlpatterns = format_suffix_patterns(urlpatterns)
Adding pagination
装有users和code的snippets的视图列表最终能返回许多实例,因此我们真的要确保对结果进行分页,允许的API客户端逐个遍历每一个独立的页面.
可以该变默认的列表样式以使用分页,通过稍微修改app内的settings文件:
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 }
注意,REST framework中所有的设定都命名在一个独立的字典REST_FRAMEWORK里,它帮助我们跟其他项目的设置很好的分离开。
如果必要的话,我们也可以定制分页形式,但在这个事例中只是处理默认的分页。