Tutorial 5: Relationships & Hyperlinked APIs

At the moment relationships within our API are represented by using primary keys. In this part of the tutorial we'll improve the cohesion and discoverability of our API, by instead using hyperlinking for relationships.

目前为止,在我们的 API 中使用主键来代表数据的关联性。在这部分,我们会改进 API 的内聚性,我们表达关联使用超链接来替代主键。

Creating an endpoint for the root of our API

Right now we have endpoints for 'snippets' and 'users', but we don't have a single entry point to our API. To create one, we'll use a regular function-based view and the @api_view decorator we introduced earlier.

现在,我们有 sinippets 和 users 的端点, 但是我们没有 API 的单入口。我们使用之前引入的原生的基于函数的视图 和 @api_view 的装饰器。

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)
            }
           )
Notice that we're using REST framework's reverse function in order to return fully-qualified URLs.
注意:我们使用reverse函数是为了返回 完整的URL。

Creating an endpoint for the highlighted snippets

The other obvious thing that's still missing from our pastebin API is the code highlighting endpoints.

Unlike all our other API endpoints, we don't want to use JSON, but instead just present an HTML representation. There are two styles of HTML renderer provided by REST framework, one for dealing with HTML rendered using templates, the other for dealing with pre-rendered HTML. The second renderer is the one we'd like to use for this endpoint.

The other thing we need to consider when creating the code highlight view is that there's no existing concrete generic view that we can use. We're not returning an object instance, but instead a property of an object instance.

很明显,API 还是缺少 hightlighting 端点。 不像API 其它的端点, 我们不使用JSON, 只使用 HTML 呈现。 HTML 有两种渲染方式, 一种使用模板,另一种是预渲染。我们使用第二种方式。

我们需要考虑的另一件事情是,当建立 highlight 视图函数时,我们能用的泛型类并不存在。我们不能返回一个实例,只能返回实例的属性。

Instead of using a concrete generic view, we'll use the base class for representing instances, and create our own .get() method. In your snippets.views add:

我们使用基类来代表实例,然后建立 get() 方法,在 snippets.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)
 

As usual we need to add the new views that we've created in to our URLconf. We'll add a url pattern for our new API root:

跟平常一样,我们需要添加新的视图的URL,编辑 urls.py:

url(r'^$','api_root'),

And then add a url pattern for the snippet highlights:

为 snippet highlights 添加 url

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

Hyperlinking our API

Dealing with relationships between entities is one of the more challenging aspects of Web API design. There are a number of different ways that we might choose to represent a relationship:

  • 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 supports all of these styles, and can apply them across forward or reverse relationships, or apply them across custom managers such as generic foreign keys.

In this case we'd like to use a hyperlinked style between entities. In order to do so, we'll modify our serializers to extend HyperlinkedModelSerializer instead of the existing ModelSerializer.

在 Web API 中处理实例之间的关系是很有挑战性的。有数个不同的方法来代表实体之间的关系:

  • 使用主键。
  • 使用超链接
  • 使用唯一的识别域
  • 使用默认的字符串
  • 在实体中嵌入相关联的实体
  • 其他自定义

REST 框架提供了以上所有的方式,而且能在正向和逆向的关系中能够使用它们,或者是外键关系。

在这个例子中,我们将使用超链接方式。改些 serializers, 让其继承 HyperlinkedModelSerializer而不是 ModelSerializer。

The HyperlinkedModelSerializer has the following differences from ModelSerializer:

HyperlinkedModelSerializer 与  ModelSerializer的不同:

  • It does not include the pk field by default.
  • It includes a url field, using HyperlinkedIdentityField.
  • Relationships use HyperlinkedRelatedField, instead of PrimaryKeyRelatedField.
  • 默认不包括主键。
  • 包括 url 域,使用HyperlinkedIdentityField。
  • 关系使用 HyperlinkedRelatedField 而不是 PrimaryKeyRelatedField。

We can easily re-write our existing serializers to use hyperlinking.

重写 serializers:

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')

Notice that we've also added a new 'highlight' field. This field is of the same type as the url field, except that it points to the 'snippet-highlight' url pattern, instead of the 'snippet-detail' url pattern.

注意:我们也添加了一个新的 'highlight' 域。该域和 url 域是一样的类型,只不过它指向的是 'snippet-highlight' 的URL 而不是 'snippet-detail'的。

Because we've included format suffixed URLs such as '.json', we also need to indicate on the highlight field that any format suffixed hyperlinks it returns should use the '.html' suffix.

由于我们已经包含了 URL 的后缀格式, 像 '.json',我们同样需要指明 hightlight 域返回的后缀格式,这里使用的是'.html'。

Making sure our URL patterns are named

If we're going to have a hyperlinked API, we need to make sure we name our URL patterns. Let's take a look at which URL patterns we need to name.

  • The root of our API refers to 'user-list' and 'snippet-list'.
  • Our snippet serializer includes a field that refers to 'snippet-highlight'.
  • Our user serializer includes a field that refers to 'snippet-detail'.
  • Our snippet and user serializers include 'url' fields that by default will refer to '{model_name}-detail', which in this case will be 'snippet-detail' and 'user-detail'.

如果我们有一个超链接 API, 我们需要确保命名 URL 模式。 看看哪些 URL 模式需要命名:

  • ‘user-list’ 和 'snippet-list'
  • snippet serializer包含的 'snippet-highlight'
  • user serializer包含的'snippet-detail'
  • snippet 和 user serializer 包含的 ‘url’ 域 默认 指向 ‘{model_name}-detail’,将其改成 'snippet-detail'和'user-detail'。

After adding all those names into our URLconf, our final 'urls.py' file should look something like this:

更改后的 urls.py 文件:

# API endpoints
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')))# Login and logout views for the browsable API
urlpatterns += patterns('',
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),)
 

Adding pagination

The list views for users and code snippets could end up returning quite a lot of instances, so really we'd like to make sure we paginate the results, and allow the API client to step through each of the individual pages.

users 和 snippets 可能返回很多内容,所有我们需要对结果进行分页,允许 API 客户端进行分页。

We can change the default list style to use pagination, by modifying our settings.py file slightly. Add the following setting:

我们更改默认的分页方式,在 settings.py 文件中加入:

REST_FRAMEWORK ={'PAGINATE_BY':10}

Note that settings in REST framework are all namespaced into a single dictionary setting, named 'REST_FRAMEWORK', which helps keep them well separated from your other project settings.

We could also customize the pagination style if we needed too, but in this case we'll just stick with the default.

注意,REST 框架所有的设置被保存在  ‘REST_FRAMEWORK’ 的字典中,这样可以帮助我们区别于工程中的其他设置。我们也可以自定义分页方式,在这个例子中,我们只是使用默认的方式。

Browsing the API

If we open a browser and navigate to the browsable API, you'll find that you can now work your way around the API simply by following links.

You'll also be able to see the 'highlight' links on the snippet instances, that will take you to the highlighted code HTML representations.

如果打开浏览器,浏览 Web API,你会发现可以正常工作呢。同样可以看snippet实例的 ‘highlight’ 的链接,它会以 HTML 方式展现。

In part 6 of the tutorial we'll look at how we can use ViewSets and Routers to reduce the amount of code we need to build our API.

在第六章,我们将看到怎样使用 ViewSets和Routers 来减少API的代码。

5,基于关系和超链接的 API的更多相关文章

  1. django rest framwork教程之外键关系和超链接

    此时,我们的API中的关系通过使用主键来表示.在本教程的这一部分中,我们将通过使用超链接来改善关系的内聚性和可发现性 为我们的API的根创建一个端点 现在我们有"snippets" ...

  2. 基于语义感知SBST的API场景测试智能生成

    摘要:面对庞大服务接口群,完备的接口测试覆盖和业务上下文场景测试看护才有可能保障产品服务的质量和可信.如果你想低成本实现产品和服务的测试高覆盖和高质量看护,这篇文章将为你提供你想要的. 本文分享自华为 ...

  3. CSS3 基于关系的选择器

    常见的基于关系的选择器 选择器 选择的元素 A E 元素A的任一后代元素E (后代节点指A的子节点,子节点的子节点,以此类推) A > E 元素A的任一子元素E(也就是直系后代) E:first ...

  4. 使用 node-odata 轻松创建基于 OData 协议的 RESTful API

    前言 OData, 相信身为.NET程序员应该不为陌生, 对于他的实现, 之前也有童鞋进行过介绍(见:这里1,这里2). 微软的WCF Data Service即采用的该协议来进行通信, ASP.NE ...

  5. T-SQL 基于关系分割字符串

    今天晚上学习了下 SQL 基于关系的运算,同时也捉摸着写了个例子,虽然我知道性能不是很好,还有待优化.直接上源代码吧,思路表达出来有点困难,直接贴上代码,如果有人不懂得可以MM 我. declare ...

  6. PhantomJS是一个基于WebKit的服务器端JavaScript API

    PhantomJS是一个基于WebKit的服务器端JavaScript API,它基于 BSD开源协议发布.PhantomJS无需浏览器的支持即可实现对Web的支持,且原生支持各种Web标准,如DOM ...

  7. 基于nginx+lua+redis高性能api应用实践

    基于nginx+lua+redis高性能api应用实践 前言 比较传统的服务端程序(PHP.FAST CGI等),大多都是通过每产生一个请求,都会有一个进程与之相对应,请求处理完毕后相关进程自动释放. ...

  8. 转载 基于JAVA每月运势api调用代码实例

    代码描述:基于JAVA每月运势api调用代码实例 接口地址:http://www.juhe.cn/docs/api/id/58 原文链接:http://outofmemory.cn/code-snip ...

  9. bloom-server 基于 rust 编写的 rest api cache 中间件

    bloom-server 基于 rust 编写的 rest api cache 中间件,他位于lb 与api worker 之间,使用redis 作为缓存内容存储, 我们需要做的就是配置proxy,同 ...

随机推荐

  1. DbEntry.Net(Lephone Framework) Access ORM:安装和简单使用

    项目中用到Access数据库,之前用的普通Ado.Net 三层.遇到表字段叫多时,就比较费力.想要使用ORM,无奈EF不支持Access.虽然可以改写linq to sql为Linq to Acces ...

  2. C#求百分比

    public string integralpercentage; integralpercentage = ((double)user.Credits / integralmax).ToString ...

  3. MapReduce:给出children-parents(孩子——父母)表,要求输出grandchild-grandparent(孙子——爷奶)表

    hadoop中使用MapReduce单表关联案例: MapReduce:给出children-parents(孩子——父母)表,要求输出grandchild-grandparent(孙子——爷奶)表. ...

  4. mysql配置文件生效顺序

    安装完数据库 除了将my.cnf放在/etc/下放在其他地方也是可以的 cp /usr/share/mysql/my-default.cnf /etc/my.cnf 今天就看一下这些my.cnf是怎么 ...

  5. virtualbox ubuntu下ssh连接

    一.首先Ubuntu中安装ssh服务器 Ubuntu 下安装 OpenSSH Server 是无比轻松的一件事情,需要的命令只有一条: sudo apt-get install openssh-ser ...

  6. 简单Trace类实现

    <C++沉思录>27章内容修改后所得: /************************************************************************/ ...

  7. tensorflow笔记:使用tf来实现word2vec

    (一) tensorflow笔记:流程,概念和简单代码注释 (二) tensorflow笔记:多层CNN代码分析 (三) tensorflow笔记:多层LSTM代码分析 (四) tensorflow笔 ...

  8. python学习笔记(生成xml)

    想着给框架加些功能 首先想到的是生成测试报告 这里就涉及到了生成什么格式的文件 我这边就准备生成 xml 格式的文件 自己先学习了整理了下 代码如下: #!/usr/bin/env python # ...

  9. C++(十六) — 类中引用成员函数、命名空间的使用

    1.为什么类中引用成员函数? 类将属性和方法做了封装.类是一种数据类型,也就是:固定大小内存块的别名. 类的定义是一个抽象的概念,定义时不分配内存,当用类定义对象时,才分配一个固定大小的内存块. 此时 ...

  10. appium自动化测试(三)

    一. 层级定位和list 先通过find_element_by_XXX找到父级元素webelement,再通过webelement.find_element_by_XXX寻找子元素 二. 滑动屏幕 滑 ...