前言

在第四篇中,加入了用户模型,以及相关的认证和权限的功能。但是我们在使用的时候,会发现在访问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并添加下面的内容:

  1. from rest_framework.decorators import api_view
  2. from rest_framework.response import Response
  3. from rest_framework.reverse import reverse
  4.  
  5. @api_view(['GET'])
  6. def api_root(request, format=None):
  7. return Response({
  8. 'users': reverse('user-list', request=request, format=format),
  9. 'snippets': reverse('snippet-list', request=request, format=format)
  10. })

关于装饰器在之前的文章已经讲解过了,这里的新知识是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代码,也就是下面这段代码:

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

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

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

  1. from rest_framework import renderers
  2.  
  3. class SnippetHighlight(generics.GenericAPIView):
  4. queryset = Snippet.objects.all()
  5. renderer_classes = (renderers.StaticHTMLRenderer,)
  6.  
  7. def get(self, request, *args, **kwargs):
  8. snippet = self.get_object()
  9. return Response(snippet.highlighted)

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

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

  1. renderer_classes = (renderers.StaticHTMLRenderer,)

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

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

  1. url(r'^$', views.api_root),
  2. 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,改进序列化器:

  1. class SnippetSerializer(serializers.HyperlinkedModelSerializer):
  2. owner = serializers.ReadOnlyField(source='owner.username')
  3. highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')
  4.  
  5. class Meta:
  6. model = Snippet
  7. fields = ('url', 'id', 'highlight', 'owner',
  8. 'title', 'code', 'linenos', 'language', 'style')
  9.  
  10. class UserSerializer(serializers.HyperlinkedModelSerializer):
  11. snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)
  12.  
  13. class Meta:
  14. model = User
  15. fields = ('url', 'id', 'username', 'snippets')

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

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

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

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

  1. try:
  2. url = self.get_url(value, self.view_name, request, format)
  3. except NoReverseMatch:
  4. ...
  5.  
  6. if url is None:
  7. return None
  8.  
  9. return Hyperlink(url, value)

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

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


为各个URL模式命名

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

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

  1. from django.conf.urls import url
  2. from snippets import views
  3. from rest_framework.urlpatterns import format_suffix_patterns
  4.  
  5. urlpatterns = [
  6. url(r'^snippets/$', views.SnippetList.as_view(),name='snippet-list'),
  7. url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view(),name='snippet-detail'),
  8. url(r'^users/$',views.UserList.as_view(),name='user-list'),
  9. url(r'^users/(?P<pk>[0-9]+)/$',views.UserDetail.as_view(),name='user-detail'),
  10. url(r'^$',views.api_root),
  11. url(r'^snippets/(?P<pk>[0-9]+)/highlighted/$',views.SnippetHighlight.as_view(),name='snippet-highlighted'),
  12. ]
  13.  
  14. urlpatterns = format_suffix_patterns(urlpatterns)

添加分页

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

  1. REST_FRAMEWORK = {
  2. 'PAGE_SIZE': 10
  3. }

这样就可以实现分页了


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. Phpcms 前台页面实现分页

    phpcms开发就是模仿里面原有的方法进行扩展,前台要实现分页,就去找后台页面的分页实现. 如后台 扩展->后台操作日志,就有分页展示. 1.先去添加自己的分页方法(千万不要在原来的方法上修改, ...

  2. nopCommerce 3.9 大波浪系列 之 IWebHelper

    接口:Nop.Core.IWebHelper 实现:Nop.Core.WebHelper 测试:Nop.Core.Tests.WebHelperTests 简介:Web辅助类 功能:获取客户端IP地址 ...

  3. hdu_5963:朋友

    刚看到这题时感觉是树上博弈,然后我开始用一维的数据找规律.发现在一维的树上,如果把各边的值合在一起当成一个二进制数,那么,ans只与奇偶性有关,于是,我提出了一个比较大胆的假设:若连接在root上的所 ...

  4. nyoj_5:Binary String Matching

    简单字符串匹配 题目链接 #include<iostream> #include<cstring> using namespace std; int fun(char*aa,c ...

  5. nopCommerce 3.9 大波浪系列 之 路由扩展 [多语言Seo的实现]

    一.nop种的路由注册 在Global.asax,Application_Start()方法中会进行路由注册,代码如下. public static void RegisterRoutes(Route ...

  6. 决策树(C4.5)原理

    决策树c4.5算法是在决策树ID3上面演变而来. 在ID3中: 信息增益 按属性A划分数据集S的信息增益Gain(S,A)为样本集S的熵减去按属性A划分S后的样本子集的熵,即 在此基础上,C4.5计算 ...

  7. Intellij IDEA 2017 详细图文教程之概述

    天天编码 , 版权所有丨本文标题:Intellij IDEA 2017 详细图文教程之概述 转载请保留页面地址:http://www.tiantianbianma.com/intellij-idea- ...

  8. 初学 Python(十二)——高阶函数

    初学 Python(十二)--高阶函数 初学 Python,主要整理一些学习到的知识点,这次是高阶函数. #-*- coding:utf-8 -*- ''''' 话说高阶函数: 能用函数作为参数的函数 ...

  9. 如何将R包安装到自定义路径

    参考  设置环境变量R_LIBS将R包安装到自定义路径   实际上是可以解决问题的, #环境变量完成以后,启动(重启)R,运行 .libPaths() 加载R包时,发现路径仍然未变成自定义的. 那么参 ...

  10. HDU 2202 最大三角形(凸包)

    Problem Description 老师在计算几何这门课上给Eddy布置了一道题目,题目是这样的:给定二维的平面上n个不同的点,要求在这些点里寻找三个点,使他们构成的三角形拥有的面积最大.Eddy ...