第 7 篇:文章详情的 API 接口
一旦我们使用了视图集,并实现了 HTTP 请求对应的 action 方法(对应规则的说明见 使用视图集简化代码),将其在路由器中注册后,django-restframework 自动会自动为我们生成对应的 API 接口。
目前为止,我们只实现了 GET 请求对应的 action——list 方法,因此路由器只为我们生成了一个 API,这个 API 返回文章资源列表。GET 请求还可以用于获取单个资源,对应的 action 为 retrieve,因此,只要我们在视图集中实现 retrieve 方法的逻辑,就可以直接生成获取单篇文章资源的 API 接口。
贴心的是,django-rest-framework 已经帮我们把 retrieve 的逻辑在 mixins.RetrieveModelMixin
里写好了,直接混入视图集即可:
class PostViewSet(
mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
):
serializer_class = PostListSerializer
queryset = Post.objects.all()
permission_classes = [AllowAny]
现在,路由会自动增加一个 /posts/:pk/ 的 URL 模式,其中 pk 为文章的 id。访问此 API 接口可以获得指定文章 id 的资源。
实际上,实现各个 action 逻辑的混入类都非常简单,以 RetrieveModelMixin
为例,我们来看看它的源码:
class RetrieveModelMixin:
"""
Retrieve a model instance.
"""
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
retrieve 方法首先调用 get_object
方法获取需序列化的对象。get_object
方法通常情况下依据以下两点来筛选出单个资源对象:
get_queryset
方法(或者queryset
属性,get_queryset
方法返回的值优先)返回的资源列表对象。lookup_field
属性指定的资源筛选字段(默认为 pk)。django-rest-framework 以该字段的值从get_queryset
返回的资源列表中筛选出单个资源对象。lookup_field
字段的值将从请求的 URL 中捕获,所以你看到文章接口的 url 模式为 /posts/:pk/,假设将lookup_field
指定为 title,则 url 模式为 /posts/:title/,此时将根据文章标题获取单篇文章资源。
文章详情 Serializer
现在,假设我们要获取 id 为 1 的文章资源,访问获取单篇文章资源的 API 接口 http://127.0.0.1:10000/api/posts/1/,得到如下的返回结果:
可以看到很多我们需要在详情页中展示的字段值并没有返回,比如文章正文(body)。原因是视图集中指定的文章序列化器为 PostListSerializer,这个序列化器被用于序列化文章列表。因为展示文章列表数据时,有些字段用不上,所以出于性能考虑,只序列化了部分字段。
显然,我们需要给文章详情写一个新的序列化器了:
from .models import Category, Post, Tag
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = [
"id",
"name",
]
class PostRetrieveSerializer(serializers.ModelSerializer):
category = CategorySerializer()
author = UserSerializer()
tags = TagSerializer(many=True)
class Meta:
model = Post
fields = [
"id",
"title",
"body",
"created_time",
"modified_time",
"excerpt",
"views",
"category",
"author",
"tags",
]
详情序列化器和列表序列化器几乎一样,只是在 fields 中指定了更多需要序列化的字段。
同时注意,为了序列化文章的标签 tags,我们新增了一个 TagSerializer
,由于文章可能有多个标签,因为 tags 是一个列表,要序列化一个列表资源,需要将序列化器参数 many
的值指定为 True
。
动态 Serializer
现在新的序列化器写好了,可是在哪里指定呢?视图集中 serializer_class
属性已经被指定为了 PostListSerializer
,那 PostRetrieveSerializer
应该指定在哪呢?
类似于视图集类的 queryset
属性和 get_queryset
方法的关系, serializer_class
属性的值也可以通过 get_serializer_class
方法返回的值覆盖,因此我们可以根据不同的 action 动作来动态指定对应的序列化器。
那么如何在视图集中区分不同的 action 动作呢?视图集有一个 action 属性,专门用来记录当前请求对应的动作。对应关系如下:
HTTP 请求 | 对应 action 属性的值 |
---|---|
GET | list(资源列表)/ retrieve(单个资源) |
PUT | update |
PATCH | partial_update |
DELETE | destory |
因此,我们在视图集中重写 get_serializer_class
方法,写入我们自己的逻辑,就可以根据不同请求,分别获取相应的序列化器了:
class PostViewSet(
mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
):
# ... 省略其他属性和方法
def get_serializer_class():
if self.action == 'list':
return PostListSerializer
elif self.action == 'retrieve':
return PostRetrieveSerializer
else:
return super().get_serializer_class()
后续对于其他动作,可以再加 elif 判断,不过如果动作变多了,就会有很多的 if 判断。更好的做好是,给视图集加一个属性,用于配置 action 和 serializer_class 的对应关系,通过查表法查找 action 应该使用的序列化器。
class PostDetailViewSet(viewsets.GenericViewSet):
# ... 省略其他属性和方法
serializer_class_table = {
'list': PostListSerializer,
'retrieve': PostRetrieveSerializer,
}
def get_serializer_class():
return self.serializer_class_table.get(
self.action, super().get_serializer_class()
)
现在,再次访问单篇文章 API 接口,可以看到返回了更加详细的博客文章数据了:
关注公众号加入交流群
第 7 篇:文章详情的 API 接口的更多相关文章
- F5 api接口开发实战手册(二)
F5 rest api 各对象使用方式详解 本篇文章介绍rest api接口下Collection.Resource.Subcollections.SubResource的各种使用方法.如果您不了解这 ...
- 各开放平台API接口通用SDK序列文章 前言
最近两年一直在做API接口相关的工作,在平时工作中以及网上看到很多刚接触API接口调用的新人一开始会感到很不适应,要看的文档一大堆,自己要调用的接口找不着,或都找着了不知道怎么去调用,记得包括自己刚开 ...
- 怎样提供一个好的移动API接口服务/从零到一[开发篇]
引语:现在互联网那么热,你手里没几个APP都不好意思跟别人打招呼!但是,难道APP就是全能的神吗?答案是否定的,除了优雅的APP前端展示,其实核心还是服务器端.数据的保存.查询.消息的推送,无不是在服 ...
- Restful风格API接口开发springMVC篇
Restful风格的API是一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件.它主要用于客户端和服务器交互类的软件.基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机 ...
- Expo大作战(三十一)--expo sdk api之Payments(expo中的支付),翻译这篇文章傻逼了,完全不符合国内用户,我只负责翻译大家可以略过!
简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...
- HelloDjango 第 08 篇:开发博客文章详情页
作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 首页展示的是所有文章的列表,当用户看到感兴趣的文章时,他点击文章的标题或者继续阅读的按 ...
- thinkPHP中的文章详情页实现“上一篇下一篇”功能经验分享
前段时间在公司中接触到了用thinkPHP搭建的项目,其中涉及到了文章详情页上一篇下一篇翻页的功能实现效果. 因为刚接触这套框架和PHP,所以整理一下实现该功能的经验方法. 如果有不到位的地方,欢迎指 ...
- 可能是把 Java 接口讲得最通俗的一篇文章
读者春夏秋冬在抽象类的那篇文章中留言,"二哥,面试官最喜欢问的一个问题就是,'兄弟,说说抽象类和接口之间的区别?',啥时候讲讲接口呗!" 对于面向对象编程来说,抽象是一个极具魅力的 ...
- API接口通讯参数规范(2)
针对[API接口通讯参数规范]这篇文章留下的几个问题进行探讨. 问题1 试想一下,如果一个http请求返回一个500给我们,那我们是不是都不用看详情都知道该次请求发生了什么?这正是一个标准的结果码意义 ...
随机推荐
- 疯子的算法总结(九) 图论中的矩阵应用 Part 2 矩阵树 基尔霍夫矩阵定理 生成树计数 Matrix-Tree
定理: 1.设G为无向图,设矩阵D为图G的度矩阵,设C为图G的邻接矩阵. 2.对于矩阵D,D[i][j]当 i!=j 时,是一条边,对于一条边而言无度可言为0,当i==j时表示一点,代表点i的度. 即 ...
- 图论——Tarjan 初步 DFS序+时间戳+欧拉序
一.什么是DFS序: DFS序是按照先序遍历,先遍历根节点然后依次遍历左子树,右子树的过程,每次遇到新的节点就把新访问节点加到序列中,代码如下: int DFSrk[100000]; int cnt= ...
- SpringBoot:扩展SpringMVC、定制首页、国际化
目录 扩展使用SpringMVC 如何扩展SpringMVC 为何这么做会生效(原理) 全面接管SpringMVC 首页实现 页面国际化 SpringBoot扩展使用SpringMVC.使用模板引擎定 ...
- GoF23:建造者模式
目录 概念 角色分析 实现方式 方式一 角色分析 代码编写 方式二 角色分析 代码编写 总结 优点 缺点 应用场景 建造者也抽象工厂模式的比较 建造者模式也属于创建型模式,它提供了一种创建对象的最 ...
- 读CSV文件并写arcgis shp文件
一.在这里我用到的csv文件是包含x,y坐标及高程.降雨量数据的文件.如下图所示. 二.SF简介 简单要素模型(Simple Feature,SF),是 OGC 国际组织定义的面向对象的矢量数据模型. ...
- LeetCode--Jewels and Stones && Range Sum of BST (Easy)
771. Jewels and Stones (Easy)# You're given strings J representing the types of stones that are jewe ...
- Jmeter5.2.1 三种参数化
Jmeter的相关操作,一定要会参数化,常见的参数化方法有如下几种: 一.用户定义的变量 常应用于设置一些全局变量,包括url.host.port等等 线程组--添加--配置元件--用户定义的变量,添 ...
- [hdu4497]分解质因数
题意:求满足gcd(x,y,z)=G,lcm(x,y,z)=L的x,y,z的解的个数. 大致思路:首先如果L % G != 0那么无解,否则令u = L / G,问题变为,gcd(r,s,t)=1,l ...
- HDU 2013 (水)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2013 题目大意:已知最后一天桃子只有一个,告诉你猴崽子第一天吃掉总桃子数的一半多一个,第二天吃掉剩下总 ...
- SpringBatch异常To use the default BatchConfigurer the context must contain no more thanone DataSource
SpringBoot整合SpringBatch项目,已将代码开源至github,访问地址:https://github.com/cmlbeliever/SpringBatch 欢迎star or fo ...