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

推荐订阅源

IT之家
IT之家
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The GitHub Blog
The GitHub Blog
MyScale Blog
MyScale Blog
爱范儿
爱范儿
GbyAI
GbyAI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
美团技术团队
Y
Y Combinator Blog
博客园 - 叶小钗
Apple Machine Learning Research
Apple Machine Learning Research
Martin Fowler
Martin Fowler
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
罗磊的独立博客
M
MIT News - Artificial intelligence
博客园 - Franky
V
Visual Studio Blog
I
InfoQ
V
V2EX
Hugging Face - Blog
Hugging Face - Blog
腾讯CDC
博客园 - 司徒正美
L
LangChain Blog

追梦人物的博客

小度智能设备接入 Home Assistant,支持小爱同学语音控制 - 追梦人物的博客 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 教程
2020-06-28 · via 追梦人物的博客

此前我们一直在操作博客文章(Post)资源,并借此介绍了序列化器(Serializer)、视图集(Viewset)、路由器(Router)等 django-rest-framework 提供的便利工具,借助这些工具,就可以非常快速地完成 RESTful API 的开发。

评论(Comment)是另一种资源,我们同样借助以上工具来完成对评论资源的接口开发。

首先是设计评论 API 的 URL,根据 RESTful API 的设计规范,评论资源的 URL 设计为:/comments/

对评论资源的操作有获取某篇文章下的评论列表和创建评论两种操作,因此相应的 HTTP 请求和动作(action)对应如下:

HTTP请求 Action URL
GET list_comments /posts/:id/comments/
POST create /comments/

文章评论列表 API 使用自定义的 action,放在 /post/ 接口的视图集下;发表评论接口使用标准的 create action,需要定义单独的视图集。

然后需要一个序列化器,用于评论资源的序列化(获取评论时),反序列化(创建评论时)。有了编写文章序列化器的基础,评论序列化器就是依葫芦画瓢的事。

comments/serializers.py

from rest_framework import serializers
from .models import Comment


class CommentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Comment
        fields = [
            "name",
            "email",
            "url",
            "text",
            "created_time",
            "post",
        ]
        read_only_fields = [
            "created_time",
        ]
        extra_kwargs = {"post": {"write_only": True}}

注意这里我们在 Meta 中增加了 read_only_fieldsextra_kwargs 的声明。

read_only_fields 用于指定只读字段的列表,由于 created_time 是自动生成的,用于记录评论发布时间,因此声明为只读的,不允许通过接口进行修改。

extra_kwargs 指定传入每个序列化字段的额外参数,这里给 post 序列化字段传入了 write_only 关键字参数,这样就将 post 声明为只写的字段,这样 post 字段的值仅在创建评论时需要。而在返回的资源中,post 字段就不会出现。

首先来实现创建评论的接口,先为评论创建一个视图集:

comments/views.py

from rest_framework import mixins, viewsets
from .models import Comment
from .serializers import CommentSerializer

class CommentViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
    serializer_class = CommentSerializer

    def get_queryset(self):
        return Comment.objects.all()

视图集非常的简单,混入 CreateModelMixin 后,视图集就实现了标准的 create action。其实 create action 方法的实现也非常简单,我们来学习一下 CreateModelMixin 的源码实现。

class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

核心逻辑在 create 方法:首先取到绑定了用户提交数据的序列化器,用于反序列化。接着调用 is_valid 方法校验数据合法性,如果不合法,会直接抛出异常(raise_exception=True)。否则就执行序列化的 save 逻辑将评论数据存入数据库,最后返回响应。

接着在 router 里注册 CommentViewSet 视图集:

router.register(r"comments", comments.views.CommentViewSet, basename="comment")

进入 API 交互后台,可以看到首页列出了 comments 接口的 URL,点击进入 /comments/ 后可以看到一个评论表单,在这里可以提交评论数据与创建评论的接口进行交互。

接下来实现获取评论列表的接口。通常情况下,我们都是只获取某篇博客文章下的评论列表,因此我们的 API 设计成了 /posts/:id/comments/。这个接口具有很强的语义,非常符合 RESTful API 的设计规范。

由于接口位于 /posts/ 空间下,因此我们在 PostViewSet 添加自定义 action 来实现,先来看代码:

blog/views.py

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

    @action(
            methods=["GET"],
            detail=True,
            url_path="comments",
            url_name="comment",
            pagination_class=LimitOffsetPagination,
            serializer_class=CommentSerializer,
    )
    def list_comments(self, request, *args, **kwargs):
        # 根据 URL 传入的参数值(文章 id)获取到博客文章记录
        post = self.get_object()
        # 获取文章下关联的全部评论
        queryset = post.comment_set.all().order_by("-created_time")
        # 对评论列表进行分页,根据 URL 传入的参数获取指定页的评论
        page = self.paginate_queryset(queryset)
        # 序列化评论
        serializer = self.get_serializer(page, many=True)
        # 返回分页后的评论列表
        return self.get_paginated_response(serializer.data)

action 装饰器我们在上一篇教程中进行了详细说明,这里我们再一次接触到 action 装饰器更为深入的用法,可以看到我们除了设置 methodsdetailurl_path 这些参数外,还通过设置 pagination_classserializer_class 来覆盖原本在 PostViewSet 中设置的这些类属性的值(例如对于分页,PostViewSet 默认为我们之前设置的 PageNumberPagination,而这里我们替换为 LimitOffsetPagination)。

list_comments 方法逻辑非常清晰,注释中给出了详细的说明。另外还可以看到我们调用了一些辅助方法,例如 paginate_queryset 对查询集进行分页;get_paginated_response 返回分页后的 HTTP 响应,这些方法其实都是 GenericViewSet 提供的通用辅助方法,源码也并不复杂,如果不用这些方法,我们自己也可以轻松实现,但既然 django-rest-framework 已经为我们写好了,直接复用就行,具体的实现请大家通过阅读源码进行学习。

现在进入 API 交互后台,进入某篇文章的详细接口,例如访问 /api/posts/5/,Extra Actions 下拉框中可以看到 List comments 的选项:

点击 List comments 即可进入这篇文章下的评论列表接口,获取这篇文章的评论列表资源了:

-- EOF --