Django编写RESTful API(五):添加超链接提高模型间的关联性
前言
在第四篇中,加入了用户模型,以及相关的认证和权限的功能。但是我们在使用的时候,会发现在访问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
欢迎转载,请在明显位置给出出处及链接