别院牧志知识库 别院牧志知识库
首页
  • 基础

    • 全栈之路
    • 😎Awesome资源
  • 进阶

    • Python 工匠系列
    • 高阶知识点
  • 指南教程

    • Socket 编程
    • 异步编程
    • PEP 系列
  • 面试

    • Python 面试题
    • 2025 面试记录
    • 2022 面试记录
    • 2021 面试记录
    • 2020 面试记录
    • 2019 面试记录
    • 数据库索引原理
  • 基金

    • 基金知识
    • 基金经理
  • 细读经典

    • 德隆-三个知道
    • 孔曼子-摊大饼理论
    • 配置者说-躺赢之路
    • 资水-建立自己的投资体系
    • 反脆弱
  • Git 参考手册
  • 提问的智慧
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
首页
  • 基础

    • 全栈之路
    • 😎Awesome资源
  • 进阶

    • Python 工匠系列
    • 高阶知识点
  • 指南教程

    • Socket 编程
    • 异步编程
    • PEP 系列
  • 面试

    • Python 面试题
    • 2025 面试记录
    • 2022 面试记录
    • 2021 面试记录
    • 2020 面试记录
    • 2019 面试记录
    • 数据库索引原理
  • 基金

    • 基金知识
    • 基金经理
  • 细读经典

    • 德隆-三个知道
    • 孔曼子-摊大饼理论
    • 配置者说-躺赢之路
    • 资水-建立自己的投资体系
    • 反脆弱
  • Git 参考手册
  • 提问的智慧
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 辨析

  • Sockets编程

  • Django

    • Django 学习资料收集
    • Django-REST-framework教程中文版

      • Django-REST-framework 教程中文版
      • 快速入门
      • 教程 1:序列化
        • 介绍
        • 设置一个新虚拟环境
        • 开始
        • 创建 Model
        • 创建 Serializer 类
        • 使用 serializer
        • 使用 ModelSerializers
        • 使用 Serializer 编写常规的 Django 视图
        • 测试 API
        • 我们现在到哪了
        • 补充
      • 教程 2:Requests 和 Responses
      • 教程 3:类视图
      • 教程 4:认证和权限
      • 教程 5:Relationships 和 Hyperlinked
      • 教程 6:ViewSets 和 Routers
  • stackoverflow

  • Flask

  • 全栈之路

  • 面试

  • 代码片段

  • 异步编程

  • 😎Awesome资源

  • PEP

  • Python工匠系列

  • 高阶知识点

  • Python 学习资源待整理
  • 设计模式

  • 好“艹蛋”的 Python 呀!
  • FIFO | 待学清单📝
  • pip 安装及使用
  • 数据分析

  • 源码阅读计划

  • OOP

  • 关于 python 中的 setup.py
  • 并行分布式框架 Celery
  • 七种武器,让你的代码提高可维护性
  • 使用 pdb 调试 Python 代码
  • 每周一个 Python 标准库
  • 🐍Python
  • Django
  • Django-REST-framework教程中文版
佚名
2021-04-13
目录

教程 1:序列化

# 介绍

在这个教程中将创建一个支持代码高亮展示的 API。我们会介绍组成 REST framework 的各个组件,并且让你明白这些组件是如何相互协作的。

这篇教程会比较深入,所以开始之前你应该准备些啤酒和花生毛豆什么的。如果你仅仅想大概了解,请看快速入门。

提示

这篇教程的源码可以在encode/rest-framework-tutorial (opens new window)找到,同时完整的实现版本也提供了在线版供大家测试,请点击这里 (opens new window)。

# 设置一个新虚拟环境

首先我们使用venv (opens new window)创建一个虚拟环境,这样可以很好的和其他项目隔离所需的依赖库:

python3 -m venv env
source env/bin/activate
1
2

然后在虚拟环境中安装需要的依赖:

pip install django
pip install djangorestframework
pip install pygments # 这个用于语法高亮
    
1
2
3
4

提示

使用deactive来退出虚拟环境,更多信息请看venv 文档 (opens new window)。

# 开始

首先我们来创建一个新项目:

cd ~
django-admin startproject tutorial
cd tutorial
1
2
3

接下来创建一个 APP:

python manage.py startapp snippets
1

然后编辑tutorial/settings.py,把我们的snippets和rest_framework添加到INSTALLED_APPS:

    INSTALLED_APPS = (
        ...
        'rest_framework',
        'snippets.apps.SnippetsConfig',
    )
1
2
3
4
5

好了,我们的准备工作做完了。

# 创建 Model

首先我们创建一个Snippet模型来存储代码片段。注意:写注释是一个好编程习惯,不过为了专注于代码本身,我们在下面省略了注释。编辑snippets/models.py:

from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

    class Meta:
        ordering = ['created']
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

接下来在数据库中建表:

python manage.py makemigrations snippets
python manage.py migrate
1
2

# 创建 Serializer 类

第一步我们需要为 API 提供一个序列化以及反序列化的方法,用来把 snippet 对象转化成 json 数据格式,创建 serializers 和创建 Django 表单类似。我们在snippets目录创建一个serializers.py:

from rest_framework import serializers
from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    pk = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

    def create(self, validated_data):
        """
        如果数据合法就创建并返回一个snippet实例
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        如果数据合法就更新并返回一个存在的snippet实例
        """
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

在第一部分我们定义了序列化/反序列化的字段,creat()和update()方法则定义了当我们调用serializer.save()时如何来创建或修改一个实例。

serializer 类和 Django 的 Form 类很像,并且包含了相似的属性标识,比如required、 max_length和default。

这些标识也能控制序列化后的字段如何展示,比如渲染成 HTML。上面的{'base_template': 'textarea.html'}和在 Django 的 Form 中设定widget=widgets.Textarea是等效的。 这一点在控制 API 在浏览器中如何展示是非常有用的,我们后面的教程中会看到。

我们也可以使用ModelSerializer类来节省时间,后续也会介绍,这里我们先显式的定义 serializer。

# 使用 serializer

继续之前我们先来熟悉一下我们的 serializer 类,打开 Django shell:

python manage.py shell
1

引入相关的代码后创建 2 个 snippet 实例:

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

snippet = Snippet(code='foo = "bar"\n')
snippet.save()

snippet = Snippet(code='print "hello, world"\n')
snippet.save()
1
2
3
4
5
6
7
8
9
10

现在我们有了可以操作的实例了,让我们来序列化一个实例:

serializer = SnippetSerializer(snippet)
serializer.data
# {'pk': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
1
2
3

这里我们把 snippet 转换成了 Python 基本数据类型,接下来我们把其转换成 json 数据:

content = JSONRenderer().render(serializer.data)
content
# b'{"id": 2, "title": "", "code": "print(\\"hello, world\\")\\n", "linenos": false, "language": "python", "style": "friendly"}'
1
2
3

反序列化也类似,我们先把一个 stream 转换成 python 基本数据类型:

import io

stream = io.BytesIO(content)
data = JSONParser().parse(stream)
1
2
3
4

然后将其转换为实例:

serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.validated_data
# OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
serializer.save()
# <Snippet: Snippet object>
1
2
3
4
5
6
7

可以看出这和 Django 的 Form 多么相似,当我们使用 serializer 编写 view 时这一特效会更明显。

我们也可以序列化实例的集合,仅需要设置 serializer 的参数many=True即可:

serializer = SnippetSerializer(Snippet.objects.all(), many=True)
serializer.data
# [OrderedDict([('pk', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('pk', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]
1
2
3

# 使用 ModelSerializers

我们的SnippetSerializer类和Snippet模型有太多重复的代码,如果让代码更简洁就更好了。

Django 提供了Form类和ModelForm类,同样的,REST framework 提供了Serializer类和ModelSerializer。

让我们使用ModelSerializer来重构代码,编辑snippets/serializers.py:

    class SnippetSerializer(serializers.ModelSerializer):
        class Meta:
            model = Snippet
            fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
1
2
3
4

我们可以通过打印输出来检查 serializer 包含哪些字段,打开 Django shell 并输入一下代码:

from snippets.serializers import SnippetSerializer
serializer = SnippetSerializer()
print(repr(serializer))
# SnippetSerializer():
#    id = IntegerField(label='ID', read_only=True)
#    title = CharField(allow_blank=True, max_length=100, required=False)
#    code = CharField(style={'base_template': 'textarea.html'})
#    linenos = BooleanField(required=False)
#    language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...
#    style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...

1
2
3
4
5
6
7
8
9
10
11

请记住ModelSerializer并没有使用任何黑科技,它仅仅是一个创建 serializer 类的简单方法:

  • 自动检测字段
  • 简单的定义了create()和update()方法。

# 使用 Serializer 编写常规的 Django 视图

现在我们来使用 Serializer 类来编写 API 视图,这里我们不使用任何 REST framewrok 的其他特性,仅使用 Django 的常规方法编写视图。

首先我们创建一个HttpResponse的子类来返回 json 类型数据。

编辑snippets/views.py:

from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
1
2
3
4
5

我们 API 的根目录是一个 list view, 用于展示所有存在的 snippet, 或建立新的 snippet:

@csrf_exempt
def snippet_list(request):
    """
    展示所以snippets,或创建新的snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JSONResponse(serializer.data)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data, status=201)
        return JSONResponse(serializer.errors, status=400)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

注意,这里我们为了能简单的在客户端进行 POST 操作而使用了csrf_exempt,正常情况下你不应该这么做,REST framework 提供了更安全的做法。

我们也需要一个页面用来展示、修改或删除某个具体的 snippet:

@csrf_exempt
def snippet_detail(request, pk):
    """
    修改或删除一个snippet.
    """
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return JSONResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(snippet, data=data)
        if serializer.is_valid():
            serializer.save()
            return JSONResponse(serializer.data)
        return JSONResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        snippet.delete()
        return HttpResponse(status=204)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

接下来修改snippets/urls.py:

from django.urls import path
from snippets import views

urlpatterns = [
    path('snippets/', views.snippet_list),
    path('snippets/<int:pk>/', views.snippet_detail),
]
1
2
3
4
5
6
7

我们还需要在tutorial/urls.py文件中连接根 urlconf,以包含我们的代码段应用程序的 URL。

from django.urls import path, include

urlpatterns = [
    path('', include('snippets.urls')),
]
1
2
3
4
5

这里我们有许多细节没有处理,比如畸形的 JSON 数据、不支持的 HTTP 请求方法,这里我们都暂时返回 500 错误。

# 测试 API

现在我们可以启动我们的服务器了。

首先使用quit()退出 shell,然后启动服务:

python manage.py runserver

Validating models...

0 errors found
Django version 1.8.3, using settings 'tutorial.settings'
Development server is running at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
1
2
3
4
5
6
7
8

提示

如果需要内网其他主机可以访问本机信息,我们则需要python manager.py runserver 0.0.0.0:8000指定监听 host 为0.0.0.0,同时在防火墙开放相应端口。注意,这个时候我们要访问的 ip 地址应该改为本机(服务所在机器)的 ip 地址而不是127.0.0.1的地址。

打开另一个终端,我们可以使用 curl 或 httpie 来进行测试。httpie 是一个用 python 编写的易用的 http 客户端,首先来安装 httpie:

pip install httpie
1

最终,我们得到了一个 snippets 列表:

http http://127.0.0.1:8000/snippets/

HTTP/1.1 200 OK
...
[
    {
    "id": 1,
    "title": "",
    "code": "foo = \"bar\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
    },
    {
    "id": 2,
    "title": "",
    "code": "print \"hello, world\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
    }
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

或者通过 ID 来获取某个特定的 snippets:

http http://127.0.0.1:8000/snippets/2/

HTTP/1.1 200 OK
...
{
    "id": 2,
    "title": "",
    "code": "print \"hello, world\"\n",
    "linenos": false,
    "language": "python",
    "style": "friendly"
}
1
2
3
4
5
6
7
8
9
10
11
12

同样的,你可以使用浏览器访问那些 URL。

# 我们现在到哪了

到目前为止,我们做的还不错,我们有一个与 Django 的 Forms API 非常相似的序列化 API,还有一些常规的 Django 视图。

我们的 API 视图目前没有做任何特别的事情,除了提供 json 响应,还有一些错误处理的边缘情况我们仍然想要清理,但它是一个功能良好的 Web API。

在本教程的第 2 部分中,我们将看到如何开始改进。


# 补充

django rest framework serializers 小结 (opens new window)

编辑 (opens new window)
#DRF
上次更新: 2024-07-23, 01:00:43
快速入门
教程 2:Requests 和 Responses

← 快速入门 教程 2:Requests 和 Responses→

最近更新
01
2025 面试记录
05-28
02
提升沟通亲和力的实用策略
03-26
03
工作
07-15
更多文章>
Theme by Vdoing | Copyright © 2019-2025 IMOYAO | 别院牧志
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式