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里,它帮助我们跟其他项目的设置很好的分离开。

如果必要的话,我们也可以定制分页形式,但在这个事例中只是处理默认的分页。

上一篇:Linux 线程 条件变量


下一篇:SGU 239.Minesweeper