前言

在第四篇中,加入了用户模型,以及相关的认证和权限的功能。但是我们在使用的时候,会发现在访问http://127.0.0.1:8000/users/时看到的用户列表,不能够直接点击某个链接然后查看其详情,也就是不能跳转到http://127.0.0.1:8000/users/2这样的链接,查看Snippet列表的时候也是如此。而且User和Snippet也没相关的链接进行相互之间的跳转。这些就很影响用户体验了,每次都需要重新输入URL才可以访问别的内容。这就是这篇文章主要解决的问题。

另外,上一篇文章说的能使代码段高亮的HTML代码,也会在本文中看到其使用。


为API创建根URL

根URL也就是访问根路径,就是http://127.0.0.1:8000/,要让这个页面能显示并访问所有的模型,也就是本项目的snippets和users。所以在views.py中肯定要多增加一个内容作为根URL(也就是首页)的视图,在这里我们采用基于函数的视图,编辑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)
})

关于装饰器在之前的文章已经讲解过了,这里的新知识是reverse,这是rest_framework的reverse而不是Django自带的那个,但是使用习惯类似,它会根据参数返回一个超链接,看到'user-list'和'snippet-list'基本就和Django自带的reverse一样的道理,就是根据路由匹配模式的命名来生成超链接,所以等下需要编辑snippets/urls.py设置一下name参数。

然后Response的参数是一个字典,这个其实也和Django开发一样,这个字典的键和值会传到前端模板然后经过模板引擎渲染,只不过这里的前端模板django-rest-framework已经帮我们做好了,只需把值传递过去就OK啦。


创建跳转至查看高亮代码段的URL

现在我们的API还不能查看高亮代码段,所以需要添加一个链接进行跳转。

回到上一篇文章里面的snippets/models.py,我们为Snippet模型添加了highlighted字段,并且使用save方法,使得保存数据时生成能使代码段高亮的HTML代码,也就是下面这段代码:

def save(self, *args, **kwargs):
"""
使用pygments库来生成能使代码高亮的HTML代码
"""
lexer = get_lexer_by_name(self.language)
linenos = self.linenos and 'table' or False
options = self.title and {'title': self.title} or {}
formatter = HtmlFormatter(style=self.style, linenos=linenos,
full=True, **options)
self.highlighted = highlight(self.code, lexer, formatter)
super(Snippet, self).save(*args, **kwargs)

所以每次保存数据时都会自动更新生成新的HTML代码。

现在我们要做的就是使用API的时候,每个snippet下面除了id、title、owner等这些本来就有的,还要加一个超链接,点击链接就能查看高亮代码段的页面,所以需要为这个新页面再创建一个视图,编辑snippet/views.py,添加代码:

from rest_framework import renderers

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)

这个代码高亮是为了在浏览器上使用API时查看的,所以返回json格式的数据就没有什么意思了,所以这里限定为只用HTML方式呈现。

REST framework为我们提供了两种方式来呈现HTML,一种是使用已有的模板(我们平时开发Django更常用的那种方式),另一种就是使用已经构建好的HTML代码。在这里我们会使用第二种方法,因为刚才已经说了每次保存数据时都会自动更新生成新的HTML代码,而这个由pygments生成的代码就保存在Snippet下的highlighted,所以有浏览器渲染并呈现highlighted下的HTML代码就行了。因此有:

renderer_classes = (renderers.StaticHTMLRenderer,)

另外我们还注意到这里使用了get方法,其他的视图类不用这个方法因为他们返回的是整个实例对象,而我们的高亮代码段页面只需要这个实例对象的一个属性,也就是snippet.highlighted。REST framework提供的通用视图类并没有提供直接返回一个实例的某个属性的方法,所以这里需要我们自己写一个get方法来指定返回的属性。

完成了根视图以及高亮代码段视图的设计,要调用到它们的话,接下来自然要为其设计URL了。编辑snippets/urls.py,添加下面两个url模式:

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

用超链接关联API

到目前,User和Snippet在浏览时还不能相互之间进行跳转,比如我们访问一个用户的详情页时,单个User下的snippets会显示此用户创建的所有snippet,但是只显示了id值,可读性不好并且不能跳转,光看到个数字其实意义不大。我们希望实现的是把这些id值换成相应的snippet的超链接,同时希望在查看用户列表的时候每个用户下面有个超链接能直接进入该用户详情页;同样的,在每个snippet下有个URL指向其创建者的详情页面。

说了那么多,我们想要的就是用超链接来关联API,用来代替之前简单粗暴的使用外键以及id值来表示。

由此,在序列化器中引出一个新的HyperlinkedModelSerializer类来代替之前的ModelSerializer类。这个新的类有以下的不同点: 1. 默认不包含id值 2. 通过HyperlinkedIdentityField这个字段会为序列化器生成一个url属性 3. 关联API使用的是HyperlinkedRelatedField而不是PrimaryKeyRelatedField(超链接代替外键)

这么一看,这个新的HyperlinkedModelSerializer类好像可以实现上面我们所说的那些功能,确实是这样的。编辑snippet/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')

可以看到两个Meta类都多了一个'url',这就是HyperlinkedRelatedField生成的,并且看到参数中又有一个命名空间,乍一看好像有点像reverse生成URL的套路啊?

额...内部的实现真的是有用到reverse,通过查看源码就能追踪到那里,首先进入HyperlinkedRelatedField源码,发现里面只有一个__init__构造方法,那就继续进入它的父类HyperlinkedRelatedField的源码,发现里面有这么一个函数:

    def get_url(self, obj, view_name, request, format):
"""
Given an object, return the URL that hyperlinks to the object. May raise a `NoReverseMatch` if the `view_name` and `lookup_field`
attributes are not configured to correctly match the URL conf.
"""
# Unsaved objects will not yet have a valid URL.
if hasattr(obj, 'pk') and obj.pk in (None, ''):
return None lookup_value = getattr(obj, self.lookup_field)
kwargs = {self.lookup_url_kwarg: lookup_value}
return self.reverse(view_name, kwargs=kwargs, request=request, format=format)

发现其实这个方法最后用的还是reverse方法,并且将生成的url作为返回的数据。所以继续往下看这个类的代码,会发现还有个to_representation方法里面有这么几行代码:

try:
url = self.get_url(value, self.view_name, request, format)
except NoReverseMatch:
... if url is None:
return None return Hyperlink(url, value)

这个过程下来我们大概能知道HyperlinkedIdentityField也能帮我们生成相应url,并且是一个超链接的形式。

另外注意到我们想要让代码高亮API只用HTML呈现,所以还设置了format='html'参数限定了后缀。


为各个URL模式命名

上面的程序为了生成url又是reverse又是HyperlinkedIdentityField的,其中的参数都用到了命名,所以我们想要生成正确的url就要给各个URL模式根据上面的参数正确命名。

编辑 snippets/urls.py,添加命名:

from django.conf.urls import url
from snippets import views
from rest_framework.urlpatterns import format_suffix_patterns urlpatterns = [
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'^users/$',views.UserList.as_view(),name='user-list'),
url(r'^users/(?P<pk>[0-9]+)/$',views.UserDetail.as_view(),name='user-detail'),
url(r'^$',views.api_root),
url(r'^snippets/(?P<pk>[0-9]+)/highlighted/$',views.SnippetHighlight.as_view(),name='snippet-highlighted'),
] urlpatterns = format_suffix_patterns(urlpatterns)

添加分页

如果我们创建的用户和代码段都很多的话,再查看列表是全部显示在一页有时候可能有点难看,所以这里需要添加一个分页设置,很简单,只需要在项目的settings.py中添加一个配置字典:

REST_FRAMEWORK = {
'PAGE_SIZE': 10
}

这样就可以实现分页了


OK,现在我们的项目通过使用各种超链接来关联,API之间已经可以方便的进行花式跳转了。下面看一下实际的效果:

首先是API根页面:

里面的链接都是可以点击的,下面是单个Snippet详情页:

最后是代码高亮页面,其实就是highlighted中的HTML代码被浏览器渲染后的样子:

想要这个页面的源码的话除了在浏览器右键打开,还可以直接SnippetSerializer下面的Meta类中,直接为field再加一个'highlighted',然后浏览的时候就会发现Snippet详情页多了个highlighted键,它的值就是很长很长的一坨HTML代码,这代码生成的页面其实就是上面那个图的样子。


OK,关于添加超链接提高模型间的关联性的介绍就先到这了。下一篇文章会介绍视图集和路由相关的内容。

本文地址:http://www.cnblogs.com/zivwong/p/7461764.html
作者博客:ziv
欢迎转载,请在明显位置给出出处及链接

Django编写RESTful API(五):添加超链接提高模型间的关联性的更多相关文章

  1. Django编写RESTful API(四):认证和权限

    欢迎访问我的个人网站:www.comingnext.cn 前言: 按照前面几篇文章里那样做,使用Django编写RESTful API的基本功能已经像模像样了.我们可以通过不同的URL访问到不同的资源 ...

  2. Django编写RESTful API(一):序列化

    欢迎访问我的个人网站:www.comingnext.cn 关于RESTful API 现在,在开发的过程中,我们经常会听到前后端分离这个技术名词,顾名思义,就是前台的开发和后台的开发分离开.这个技术方 ...

  3. Spring Boot 2.x 编写 RESTful API (五) 单元测试

    用Spring Boot编写RESTful API 学习笔记 概念 驱动模块 被测模块 桩模块 替代尚未开发完毕的子模块 替代对环境依赖较大的子模块 (例如数据访问层) 示例 测试 Service @ ...

  4. Django编写RESTful API(二):请求和响应

    欢迎访问我的个人网站:www.comingnext.cn 前言 在上一篇文章,已经实现了访问指定URL就返回了指定的数据,这也体现了RESTful API的一个理念,每一个URL代表着一个资源.当然我 ...

  5. Django编写RESTful API(六):ViewSets和Routers

    欢迎访问我的个人网站:www.comingnext.cn 前言 在本系列的文章中,我在第一篇和第二篇文章中写的编写Django视图时,使用的都是基于函数的方法,并且每个视图函数之前都会加一个djang ...

  6. Django编写RESTful API(三):基于类的视图

    欢迎访问我的个人网站:www.comingnext.cn 前言 在上一篇文章中,主要讲的是请求和响应,项目里面views.py中的视图函数都是基于函数的,并且我们介绍了@api_view这个很有用的装 ...

  7. 利用 Django REST framework 编写 RESTful API

    利用 Django REST framework 编写 RESTful API Updateat 2015/12/3: 增加 filter 最近在玩 Django,不得不说 rest_framewor ...

  8. python 全栈开发,Day95(RESTful API介绍,基于Django实现RESTful API,DRF 序列化)

    昨日内容回顾 1. rest framework serializer(序列化)的简单使用 QuerySet([ obj, obj, obj]) --> JSON格式数据 0. 安装和导入: p ...

  9. Spring Boot 2.x 编写 RESTful API (四) 使用 Mybatis

    用Spring Boot编写RESTful API 学习笔记 添加依赖 <dependency> <groupId>org.mybatis.spring.boot</gr ...

随机推荐

  1. vue数据绑定原理

    一.定义 vue的数据双向绑定是基于Object.defineProperty方法,通过定义data属性的get和set函数来监听数据对象的变化,一旦变化,vue利用发布订阅模式,通知订阅者执行回调函 ...

  2. Spring Boot 使用Redis缓存

    本文示例源码,请看这里 Spring Cache的官方文档,请看这里 缓存存储 Spring 提供了很多缓存管理器,例如: SimpleCacheManager EhCacheCacheManager ...

  3. [noip 2015]运输计划 [LCA][树链剖分]

    用了luogu上的题目描述 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的 ...

  4. linux函数的阻塞与非阻塞IO及错误处理

    1.阻塞是指进程等待某一个事件的发生而处于等待状态不往下执行,如果等待的事件发生了则会继续执行该进程.调用系统阻塞函数可能会导致进程阻塞进入睡眠状态. 2.阻塞IO之read读取键盘输入数据 3.li ...

  5. Angular02 将数据添加到组件中

    准备:已经搭建好angular-cli环境.知道如何创建组件 一.将一个数据添加到组件中 1 创建一个新的组件 user-item 2 将组件添加到静态模板中 3 为组件添加属性,并利用构造器赋值 4 ...

  6. Spring Data JPA在Spring Boot中的应用

    1.JPA JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.他的出现主要是为了简 ...

  7. Spring源码情操陶冶-ContextLoader

    前言-阅读源码有利于陶冶情操,本文承接前文Spring源码情操陶冶-ContextLoaderListener 静态代码块内容 ContextLoader在被主动调用的时候,会执行其的一个静态块,代码 ...

  8. dubbo&hsf&spring-cloud简单介绍

    Dubbo: 简介:Dubbo是一个分布式服务框架,以及SOA治理方案.其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等. 底部NIO基于netty ...

  9. 权限管理学习 一、ASP.NET Forms身份认证

    说明:本文示例使用的VS2017和MVC5. 系统无论大小.牛逼或屌丝,一般都离不开注册.登录.那么接下来我们就来分析下用户身份认证. 简单实现登录.注销 以前在学习.net的时候不知道什么Forms ...

  10. 使用gitLab 或 github 关联本地仓库

    要先在git里面注册自己的邮箱 然后: git commit -m 是为本次提交命名 刷新gitLab 发现更新了