Django-Rest-Framework 教程: 5. 提高关联性和超链接API

在我们的博客中, 记录了我们在开发过程中所使用的技术和遇到的问题, 希望作为其他开发和设计者的一个学习交流平台.

Django-Rest-Framework 教程: 5. 提高关联性和超链接API


到目前为止, API之间的关系是以主键形式体现的, (比如打开/users/1/, 可以看到snippets中为snippet的主键). 在本篇中, 我们将使用超链接的形式, 进一步提高API的关联程度和可发现性. 首先我们补充一些路径, 是整个API结构更为完整.

1. 根路径

现在, 我们已经有了users和snippets的路径, 但对于API本身却没有一个根路径. 我们使用@api_view修饰器来创建一个function based view作为根路径:

    # snippets/views.py
    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)
        })

注意, 我们使用了django-rest-framework的reverse(), 而不是django自带的reverse(), 此时如果打开http://127.0.0.1/8000, 会报错, 因为我们还没有为url添加name, 稍后我们会添加.

在urls.py中添加对应的路径:

    # snippets/urls.py
    url(r'^$', views.api_root),

2. 高亮snippet路径

我们还需要提供高亮snippet的路径. 当然这一路径与其他不同, 我们希望使用HTML而不是JSON来呈现. Django-rest_framework为我们提供了两种方式呈现HTML, 一种是使用模板, 另一种则是已构建好的HTML文本. 由于在创建snippet时, 我们已经使用pygments将高亮的snippet转化为HTML文本储存在数据库中, 我们使用第二种方式.

由于我们返回的并不是一个object实例, 而是一个实例的某个属性, django-rest-framework没有提供该generic class based view. 因此我们需要使用基本的view, 并创建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)

在urls.py中添加对应的路径:

    # snippets/urls.py
    url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', views.SnippetHighlight.as_view()),

3. 使用超链接

处理各部件API之间的关系是一件头痛的事. django-rest_framework为我们提供了以下这些方法来表现关系:

  • 使用主键
  • 使用超链接
  • 使用相关项的slug field
  • 使用相关项的默认文本信息
  • 将子项显示在母项中
  • 其他表现方式

这次, 我们使用超链接的方式来体现user和snippet的关系. 为了实现这一方式, 我们需要改写序列器(serializers), 使用HyperlinkedModelSerializer代替原本的ModelSerializer. HyperlinkedModelSerializer相对于ModelSerializer具有以下不同:

  • HyperlinkedModelSerializer不会自动包含pk field
  • HyperlinkedModelSerializer会自动包括url field
  • 关系使用的是HyperlinkedRelatedField而不是PrimaryKeyRelatedField
    # snippets/serializers.py
    class SnippetSerializer(serializers.HyperlinkedModelSerializer):
        owner = serializers.Field(source='owner.username')
        highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')

        class Meta:
            model = 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')

注意, 我们同时添加了 "highlight" field, 它与url field使用的一样, 是HyperlinkedRelatedField, 但指向的是snippet-highlight url而不是snippet-detail url.

由于我们在url中包含了格式信息, 我们使用format='html'参数为highlight指定.html后缀.

4. 为url添加name

编辑snippets/urls.py如下:

    # snippets/urls.py
    # API 路径
    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')
    ))

    # 可浏览式登录API
    urlpatterns += patterns('',
        url(r'^api-auth/', include('rest_framework.urls',
                                   namespace='rest_framework')),
    )

5. 分页

如果数据库中的数据达到一定程度, 那么用户使用api时可能会返回大量数据, 因此, 我们最好使用分页功能:

我们可以使用django-rest-framework自带的设置选项, 使list自动使用分页:

    # tutorial/settings.py
    REST_FRAMEWORK = {
        'PAGINATE_BY': 10
    }

6. 测试

现在可以使用之前提到的方法测试我们的API了, 可以在/snippets/中看到, 原先的id已经被url代替, url和highlight field都采用超链接的形式.


原文链接: http://weiguda.com/blog/23/