Skip to content
本页目录

ModelSerializer

上一章我们借助 DRF 库,写了一个简单的序列化器:

python
class ArticleListSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(allow_blank=True, max_length=100)
    body = serializers.CharField(allow_blank=True)
    created = serializers.DateTimeField()
    updated = serializers.DateTimeField()

这个序列化器长得跟 Form 或者 Model 也太像了,甚至感觉代码都重复了。如果能再简化一下就更好了。DRF 也考虑到了这一点,提供了现成的解决方案,也就是 ModelSerializer 了。

将序列化器修改为下面这样:

python
# article/serializers.py

from rest_framework import serializers
from article.models import Article

# 父类变成了 ModelSerializer
class ArticleListSerializer(serializers.ModelSerializer):
    class Meta:
        model = Article
        fields = [
            'id',
            'title',
            'created',
        ]

ModelSerializer 的功能与上一章的 Serializer 基本一致,不同的是它额外做了些工作:

  • 自动推断需要序列化的字段及类型
  • 提供对字段数据的验证器的默认实现
  • 提供了修改数据需要用到的 .create().update() 方法的默认实现

另外我们还可以在 fileds 列表里挑选出需要的数据,以便减小数据的体积。

在浏览器中访问 http://127.0.0.1:8000/api/article/,页面中呈现的数据如下(略微排版后):

python
[
    {
        "created": "2020-06-15T09:24:18Z",
        "id": 1,
        "title": "My first post"
    },
    {
        "created": "2020-06-15T09:24:38Z",
        "id": 2,
        "title": "Another post"
    },
    {
        "created": "2020-06-15T09:24:58Z",
        "id": 3,
        "title": "Third article with awesome things"
    }
]

可以看到 Json 数据仅包含 fields 中规定的字段了。

APIView

除了对序列化器的支持以外,DRF 还提供了对视图的扩展,以便视图更好的为接口服务。

将文章的视图修改为如下:

python
# article/views.py

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
...

@api_view(['GET', 'POST'])
def article_list(request):
    if request.method == 'GET':
        articles = Article.objects.all()
        serializer = ArticleListSerializer(articles, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = ArticleListSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

主要的变化如下:

  • @api_view 装饰器允许视图接收 GETPOST 请求,以及提供如 405 Method Not Allowed 等默认实现,以便在不同的请求下进行正确的响应。
  • 返回了 Response ,该对象由 Django 原生响应体扩展而来,它可以根据内容协商来确定返回给客户端的正确内容类型。如果数据验证有误,还可以返回适当的状态码以表示当前的情况。

让我们刷新文章列表接口:

出现了可视化的接口界面!不是说好接口只返回数据吗,这是在搞毛?

这就是视图中 Response 提供的内容协商能力了。也就是说,Django 后端根据客户端请求响应的内容类型不同,自动选择适合的表现形式;浏览器请求资源时,就返回可视化的 HTML 资源表示,其他形式请求时,又可以返回 Json 纯数据的形式。这给开发带来极大的方便。

有同学就问了,那我怎么验证它是不是真的会返回 Json 数据呢?让我们发一个网络请求试试看。

在命令行中发送请求,笔者推荐 httpie 这个库,基于 Python 并且小巧美观。

重新打开一个命令行,安装方式就是万能的 pip

httpie 可以安装在全局。

bash
pip install httpie

安装成功后,试试在命令行提交一个请求:

python
C:\...> http http://127.0.0.1:8000/api/article/

# 以下为返回内容
HTTP/1.1 200 OK
Content-Length: 235
Content-Type: application/json
...

[
    {
        "created": "2020-06-15T09:24:18Z",
        "id": 1,
        "title": "My first post"
    },
    {
        "created": "2020-06-15T09:24:38Z",
        "id": 2,
        "title": "Another post"
    },
    {
        "created": "2020-06-15T09:24:58Z",
        "id": 3,
        "title": "Third article with awesome things"
    }
]

再试试新建文章:

python
C:\...> http POST http://127.0.0.1:8000/api/article/ title=PostByJson body=HelloWorld!

# 以下为返回内容
HTTP/1.1 201 Created
...
{
    "created": "2020-06-29T07:47:53.984916Z",
    "id": 4,
    "title": "PostByJson"
}

浏览文章列表、新建文章接口就完成了。

注意事项

教程为了简洁,后续发送 web 请求会采用 httpie 为主、可视化页面为辅的方式进行开发测试。

但是使用 httpie 需要一定的学习成本(并且在 Windows 命令行下有符号兼容的坑),因此本教程的读者更推荐使用 Postman,可视化界面,会点鼠标就会用,很方便。

使用 Postman 时要注意,在 Json 中单双引号含义是不同的,不要混用。

如果你确实要用 httpie,那么Windows用户注意:由于 Windows 系统旧的 Terminal 命令行与 httpie 的各种兼容问题,后续文章中凡是提到 httpie 发送请求所指的命令行,均是指 PowerShell 命令行。