惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
D
Docker
D
DataBreaches.Net
V
Vulnerabilities – Threatpost
P
Palo Alto Networks Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
IT之家
IT之家
L
LINUX DO - 热门话题
T
The Blog of Author Tim Ferriss
雷峰网
雷峰网
Project Zero
Project Zero
T
Threatpost
D
Darknet – Hacking Tools, Hacker News & Cyber Security
宝玉的分享
宝玉的分享
Stack Overflow Blog
Stack Overflow Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
Securelist
Cisco Talos Blog
Cisco Talos Blog
Google DeepMind News
Google DeepMind News
Scott Helme
Scott Helme
The Cloudflare Blog
Know Your Adversary
Know Your Adversary
T
Tor Project blog
博客园_首页
人人都是产品经理
人人都是产品经理
博客园 - 叶小钗
Security Latest
Security Latest
Cyberwarzone
Cyberwarzone
S
Schneier on Security
T
The Exploit Database - CXSecurity.com
H
Help Net Security
Simon Willison's Weblog
Simon Willison's Weblog
阮一峰的网络日志
阮一峰的网络日志
C
Cyber Attacks, Cyber Crime and Cyber Security
P
Proofpoint News Feed
AWS News Blog
AWS News Blog
The GitHub Blog
The GitHub Blog
P
Proofpoint News Feed
T
Troy Hunt's Blog
量子位
G
GRAHAM CLULEY
O
OpenAI News
Engineering at Meta
Engineering at Meta
博客园 - Franky
SecWiki News
SecWiki News
F
Fortinet All Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com

追梦人物的博客

0x05:Merkle Tree & Patricia Trie 0x04:ECDSA - 以太坊设计与实现 LeetCode 105 从前序与中序遍历构造二叉树 迭代算法原理 + 完整证明 0x03:Address - 以太坊设计与实现 0x02:Secp256k1 - 以太坊设计与实现 0x00:专栏开篇 - 以太坊设计与实现 0x01:RLP 编码 - 以太坊设计与实现 Bellman-Ford 算法原理及其在 DeFi 套利中的应用 - 追梦人物的博客 迪杰斯特拉(Dijkstra)最短路径算法原理、实现与证明 - 追梦人物的博客 实战:CEX-DEX 稳定币套利监控程序开发 - 追梦人物的博客 CEX-DEX 稳定币套利模型 - 追梦人物的博客 Uniswap 手续费和协议费机制剖析 - 追梦人物的博客 Uniswap 流动性机制及相关数学原理分析 - 追梦人物的博客 uv 替代 pyenv + pipx + poetry 环境管理实践 Rust 项目从创建到发布 - 追梦人物的博客 VS Code 调试 Python - 追梦人物的博客 比特币跨市场套利的数学模型 - 追梦人物的博客 ERC-20 相关知识点总结 - 追梦人物的博客 Django 老项目如何从 SQLite 迁到 PostgreSQL 自动生成接口文档 - HelloDjango - django REST framework 教程 单元测试 - HelloDjango - django REST framework 教程 限制接口访问频率 - HelloDjango - django REST framework 教程 API 版本管理 - HelloDjango - django REST framework 教程 如何在 Windows 下搭建高效的 django 开发环境 拓展Python Markdown - 追梦人物的博客 加缓存为接口提速 - HelloDjango - django REST framework 教程 基于 drf-haystack 实现文章搜索接口 - HelloDjango 评论接口 - HelloDjango - django REST framework 教程 在接口返回Markdown解析后的内容 - HelloDjango - django REST framework 教程 文章详情接口 - HelloDjango - django REST framework 教程 分页 - HelloDjango - django REST framework 教程 使用视图集简化代码 - HelloDjango - django REST framework 教程 用类视图实现首页 API - HelloDjango - django REST framework 教程 实现博客首页文章列表 API - HelloDjango - django REST framework 教程 初始化 RESTful API 风格的博客系统 - HelloDjango django-rest-framework 是什么鬼? - HelloDjango - django REST framework 教程 结束 or 开始? - HelloDjango Coverage.py 统计测试覆盖率 - HelloDjango - Django博客教程(第二版) 单元测试:测试评论应用 - HelloDjango - Django博客教程(第二版) 单元测试:测试 blog 应用 - HelloDjango Django Haystack 全文检索与关键词高亮 - HelloDjango Django 博客实现简单的全文搜索 - HelloDjango - Django博客教程(第二版) 开启 Django 博客的 RSS 功能 - HelloDjango 统计各个分类和标签下的文章数 - HelloDjango - Django博客教程(第二版) 稳定易用的 Django 分页库,完善分页功能 - HelloDjango 通过 Django Pagination 实现简单分页 - HelloDjango 在脚本中使用 ORM:Faker 批量生成测试数据 - HelloDjango Django 官方推荐的姿势:类视图 - HelloDjango - Django博客教程(第二版) Django 使用 union 合并不同模型(Model) 的查询集(QuerySet) 开发博客文章阅读量统计功能 - HelloDjango - Django博客教程(第二版) 使用 Docker 让部署 Django 项目更加轻松 - HelloDjango 使用 Certbot 向 Let's Encrypt 免费申请 HTTPS 证书 - HelloDjango 使用 Fabric 自动化部署 - HelloDjango 博客代码开源啦 - 追梦人物的博客 (赠书)推荐一本django书籍:Django企业开发实战 - 追梦人物的博客 Nginx+Gunicorn+Supervisor 部署 Django 博客应用 - HelloDjango 优化博客功能细节,提升使用体验 - HelloDjango - Django博客教程(第二版) 交流的桥梁:评论功能 - HelloDjango - Django博客教程(第二版) 分类、归档和标签页 - HelloDjango - Django博客教程(第二版) 页面侧边栏:使用自定义模板标签 - HelloDjango - Django博客教程(第二版) 自动生成文章摘要 - HelloDjango - Django博客教程(第二版) Markdown 文章自动生成目录,提升阅读体验 - HelloDjango - Django博客教程(第二版) 让博客支持 Markdown 语法和代码高亮 - HelloDjango 开发博客文章详情页 - HelloDjango - Django博客教程(第二版) 创作后台开启,请开始你的表演 - HelloDjango - Django博客教程(第二版) 博客从“裸奔”到“有皮肤” - HelloDjango - Django博客教程(第二版) Django 的接客之道 - HelloDjango - Django博客教程(第二版) Django 迁移、操作数据库 - HelloDjango - Django博客教程(第二版) 创建 Django 博客的数据库模型 - HelloDjango 开始进入 django 开发之旅 - HelloDjango "空空如也"的博客应用 - HelloDjango - Django博客教程(第二版) 一种自顶而下的Python装饰器设计方法 - 追梦人物的博客 Python提取支付宝和微信支付二维码 - 追梦人物的博客 2018 - 我的学生生涯最后一年回顾 - 追梦人物的博客 批量清除todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 筛选练习参考答案 - Vue 2.x Todo 教程练习参考答案 删除todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 编辑todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 添加todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 标为完成练习参考答案 - Vue 2.x Todo 教程练习参考答案 入门仪式_Hello_Vue练习参考答案 - Vue 2.x Todo 教程练习参考答案 组件化todo应用 - Vue 2.x Todo 教程 批量清除todo - Vue 2.x Todo 教程 本地存储 - Vue 2.x Todo 教程 筛选 - Vue 2.x Todo 教程 还剩多少todo未完成 - Vue 2.x Todo 教程 自定义指令实现自动聚焦 - Vue 2.x Todo 教程 全部标为完成 - Vue 2.x Todo 教程 删除todo - Vue 2.x Todo 教程 编辑todo - Vue 2.x Todo 教程 添加todo - Vue 2.x Todo 教程 标为完成 - Vue 2.x Todo 教程 UI - Vue 2.x Todo 教程 显示todo列表 - Vue 2.x Todo 教程 入门仪式:Hello Vue - Vue 2.x Todo 教程 在学习django-rest-framework时收集的学习资料推荐 - 追梦人物的博客 区块链理论与应用研究小组成员招募书 - 追梦人物的博客 Python界网红,豆瓣工程师董伟明加了我的QQ后 - 追梦人物的博客 招募Django学习小组项目组核心成员 - 追梦人物的博客
实现分类、标签、归档日期接口 - HelloDjango - django REST framework 教程
2020-06-16 · via 追梦人物的博客

我们的博客有一个侧边栏功能,分别列出博客文章的分类列表、标签列表、归档时间列表,通过点击侧边栏对应的条目,还可以进入相应的页面。例如点击某个分类,博客将跳转到该分类下全部文章列表页面。这些数据的展示都需要开发对应的接口,以便前端调用获取数据。

分类列表、标签列表实现比较简单,我们这里给出接口的设计规范,大家可以使用前几篇教程中学到的知识点轻松实现(具体实现可参考 GtiHub 上的源代码)。

分类列表接口: /categories/

标签列表接口:/tags/

归档日期列表的接口实现稍微复杂一点,因为我们需要从已有文章中归纳文章发表日期。事实上,我们在上一部教程 HelloDjango - Django博客教程(第二版) 的 页面侧边栏:使用自定义模板标签 已经讲解了如何获取归档日期列表,只是当时返回的归档日期列表直接用于模板的渲染,而这里我们需要将归档日期列表序列化后通过 API 接口返回。

具体来说,获取博客文章发表时间归档列表的方法是调用查询集(QuerySet)的 dates 方法,提取记录中的日期。核心代码就一句:

Post.objects.dates('created_time', 'month', order='DESC')

这里 Post.objects.dates 方法会返回一个列表,列表中的元素为每一篇文章(Post)的创建日期(已去重),日期都是 Python 的 date 对象,精确到月份,降序排列。

有了返回的归档日期列表,接下来就实现相应的 API 接口视图函数:

blog/views.py

from rest_framework import mixins, status, viewsets
from rest_framework.decorators import action
from rest_framework.serializers import DateField

class PostViewSet(
    mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
):
    # ...

    @action(
        methods=["GET"], detail=False, url_path="archive/dates", url_name="archive-date"
    )
    def list_archive_dates(self, request, *args, **kwargs):
        dates = Post.objects.dates("created_time", "month", order="DESC")
        date_field = DateField()
        data = [date_field.to_representation(date) for date in dates]
        return Response(data=data, status=status.HTTP_200_OK)

注意这里我们涉及到了几个以前没有详细讲解过的用法。

一是 action 装饰器,它用来装饰一个视图集中的方法,被装饰的方法会被 django-rest-framework 的路由自动注册为一个 API 接口。

回顾一下我们之前在使用视图集 viewset 时提到过 action(动作)的概念,django-rest-framework 预定义了几个标准的动作,分别为 list 获取资源列表,retrieve 获取单个资源、update 和 partial_update 更新资源、destroy 删除资源,这些 action 具体的实现方法,分别由 mixins 模块中的混入类提供。例如 用类视图实现首页 API 中我们介绍过 mixins.ListModelMixin,这个混入类提供了 list 动作对应的标准实现,即 list 方法。视图集中所有以上提及的以标准动作命名的方法,都会被 django-rest-framework 的路由自动注册为标准的 API 接口。

django-rest-framework 默认只能识别标准命名的视图集方法并将其注册为 API,但我们可以添加更多非标准的 action,而为了让 django-rest-framework 能够识别这些方法,就需要使用 action 装饰器进行装饰。

其实我们可以简单地将 action 装饰的方法看作是一个视图函数的实现,因此可以看到方法传入的第一个参数为 request 请求对象,函数体就是这个视图函数需要执行的逻辑,显然,方法最终必须要返回一个 HTTP 响应对象。

action 装饰器通常用于在视图集中添加额外的接口实现。例如这里我们已有了 PostViewSet 视图集,标准的 list 实现了获取文章资源列表的逻辑。我们想添加一个获取文章归档日期列表的接口,因此添加了一个 list_archive_dates 方法,并使用 action 进行装饰。通常如果要在视图集中添加额外的接口实现,可以使用如下的模板代码:

@action(
    methods=["allowed http method name"], 
    detail=False or True, 
    url_path="url/path", 
    url_name="url name"
)
def method_name(self, request, *args, **kwargs):
    # 接口逻辑的具体实现,返回一个 Response

通常 action 装饰器以下 4 个参数都会设置:

methods:一个列表,指定访问这个接口时允许的 HTTP 方法(GET、POST、PUT、PATCH、DELETE)

detail:True 或者 False。设置为 True,自动注册的接口 URL 中会添加一个 pk 路径参数(请看下面的示例),否则不会。

url_path:自动注册的接口 URL。

url_name:接口名,主要用于通过接口名字反解对应的 URL。

当然,我们还可以在 action 中设置所有 ViewSet 类所支持的类属性,例如 serializer_classpagination_classpermission_classes 等,用于覆盖类视图中设置的属性值。

以上是 action 用法的一个基本介绍,现在来分析一下 list_archive_dates 这个 action 来加深理解。

methods 参数指定接口需要通过 GET 方法访问,detail 为 Falseurl_path 设置为 archive/dates,因此最终自动生成的接口路由就是 /posts/archive/dates/。如果我们设置 detail 为 True,那么生成的接口路由就是 /posts/<int:pk>/archive/dates/,生成的 URL 中就会多一个 pk 路径参数。

list_archive_dates 具体的实现逻辑中,以下几点需要注意:

一是独立使用序列化字段(Field)。之前序列化字段都是在序列化器(Serializer)里面使用的,因为通常来说接口需要序列化一个对象的多个字段。而这个接口中只需要序列化一个时间字段(类型为 Python 标准库中的 datetime.date),所以没必要单独定义一个序列化器了,直接拿 django-rest-framework 提供的用于序列化时间类型的 DateField 就可以了。用法也很简单,实例化序列化字段,调用其 to_representation 方法,将需要序列化的值传入即可(其实序列化器在序列对象的多个字段时,内部也是分别调用对应序列化字段的 to_representation 方法)。

我们通过列表推导式生成一个序列化后的归档日期列表,这个列表是可被序列化的。接着我们在接口返回一个 ResponseResponse 将序列化后的结果包装返回(保存在 data 属性中),django-rest-framework 会进一步帮我们把这个 Response 中包含的数据解析为合适的格式(例如 JSON)。

status=status.HTTP_200_OK 指定这个接口返回的状态码,HTTP_200_OK 是一个预定义的常数,即 200。django-rest-framework 将常用 HTTP 请求的状态码常数预定义 status 模块里,使用预定义的变量而不是直接使用数字的好处一是增强代码可读性,二是减少硬编码。

由于 PostViewSet 视图集已经通过 django-rest-framework 的路由进行了注册,因此 list_archive_dates 也会被连带着自动注册为一个接口。启动开发服务器,访问 /posts/archive/dates/,就可以看到返回的文章归档日期列表。

文章归档日期返回结果

注意到红框圈出部分,django-rest-framework API 交互后台会识别到额外定义的 action 并将它们展示出来,点击就可以进入到相应的 API 页面。

现在,侧边栏所需要的数据接口就开发完成了,接下来实现返回某一分类、标签或者归档日期下的文章列表接口。

使用视图集简化代码 我们开发了获取全部文章的接口。事实上,分类、标签或者归档日期文章列表的 API,本质上还是返回一个文章列表资源,只不过比首页 API 返回的文章列表资源多了个“过滤”,只过滤出了指定的部分文章而已。对于这样的场景,我们可以在请求 API 时加上查询参数,django-rest-framework 解析查询参数,然后从全部文章列表中过滤出查询所指定的文章列表再返回。

这在 RESTful API 的设计中肯定是会遇到的,因此第三方库 django-filter 帮我们实现了上述所说的查询过滤功能,而且和 django-rest-framework 有很好的集成,我们可以在 django-rest-framework 中非常方便地使用 django-filter。

既然要使用它,当然是先安装它(已安装跳过):pipenv install django-filter

接着我们来配置 PostViewSet,为其设置用于过滤返回结果集的一些属性,代码如下:

from django_filters.rest_framework import DjangoFilterBackend
from .filters import PostFilter

class PostViewSet(
    mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet
):
    # ...
    filter_backends = [DjangoFilterBackend]
    filterset_class = PostFilter

非常的简单,仅仅设置了 filter_backendsfilterset_class 两个属性。其中 filter_backends 设置为 DjangoFilterBackend,这样 API 在返回结果时, django-rest-framework 会调用设置的 backend(这里是 DjangoFilterBackend) 的 filter 方法对 get_queryset 方法返回的结果进行进一步的过滤,而 DjangoFilterBackend 会依据 filterset_class(这里是 PostFilter)中定义的过滤规则来过滤查询结果集。

当然 PostFilter 还没有定义,我们来定义它。首先在 blog 应用下创建一个 filters.py 文件,用于存放自定义 filter 的代码,PostFilter 代码如下:

from django_filters import rest_framework as drf_filters

from .models import Post


class PostFilter(drf_filters.FilterSet):
    created_year = drf_filters.NumberFilter(
        field_name="created_time", lookup_expr="year"
    )
    created_month = drf_filters.NumberFilter(
        field_name="created_time", lookup_expr="month"
    )

    class Meta:
        model = Post
        fields = ["category", "tags", "created_year", "created_month"]

PostFilter 的定义和序列化器 Serializer 非常类似。

categorytags 两个过滤字段因为是 Post 模型中定义的字段,因此 django-filter 可以自动推断其过滤规则,只需要在 Meta.fields 中声明即可。

归档日期下的文章列表,我们设计的接口传递 2 个查询参数:年份和月份。由于这两个字段在 Post 中没有定义,Post 记录时间的字段为 created_time,因此我们需要显示地定义查询规则,定义的规则是:

查询参数名 = 查询参数值的类型(查询的模型字段,查询表达式)

例如示例中定义的 created_year 查询参数,查询参数值的类型为 number,即数字,查询的模型字段为 created_time,查询表达式是 year。当用户传递 created_year 查询参数时,django-filter 实际上会将以上定义的规则翻译为如下的 ORM 查询语句:

Post.objects.filter(created_time__year=created_year传递的值)

现在回到 API 交互后台,先进到 /post/ 接口下,默认返回了全部文章列表。可以看到右上角多了个过滤器(红框圈出部分)。

点击会弹出过滤参数输入的交互面板,在这里可以交互式地输入查询过滤参数的值。

例如选择如下的过滤参数,得到查询的 URL 为:

http://127.0.0.1:10000/api/posts/?category=1&tags=1&created_year=2020&created_month=1

这条查询返回创建于 2020 年 1 月,id 为 1 的分类下,id 为 1 的标签下的全部文章。

通过不同的查询参数组合,就可以得到不同的文章资源列表了。

-- EOF --