Django REST framework完全入门
Django REST framework
一个强大灵活的Django
工具包,提供了便捷的REST API
开发框架
我们用传统的django
也可以实现REST风格的api
,但是顶不住Django REST framework
实现起来太简单了
什么REST API风格
主要需要注意这几个:
- api部署的域名, 主域名或者专有域名
- 版本, 通过url地址或者请求头accept
- 路径, 只能有名词, 不能有动词
- http请求动词, get, post, update, delete
- 状态, 200, 201, 204, 400, 401, 403, 404
- 返回json格式数据
例子(仅作参考):
功能 | 路径 | 请求方式 | 响应状态码(成功) | 响应体(成功) |
---|---|---|---|---|
获取所有的书籍 | /books |
get | 200 OK | 全部书籍 |
创建书籍 | /books |
post | 201 Created | 已经创建的书籍 |
获取单个书籍 | /books/{id} |
get | 200 OK | 单个书籍 |
修改书籍 | /books/{id} |
put | 201 Created | 已经修改的单个书籍 |
删除书籍 | /books/{id} |
delete | 204 No Content | 无 |
详细的可以看这里:RESTful 架构详解
关于返回什么,可以看这里:通用设计规则
简单使用流程
在我看来,Django RST framework
的主要工作就是对传统django
中的view
和form
在进行了通用性设计。由于使用REST API,我们可以统一使用一样的请求方式,请求参数和请求体,而响应体和状态码也是确定。所以Django RST framework
的复用性极高,而且Django RST framework
提供了从简单到复杂的封装类,我们只需要直接使用这些类或继承这些类重写部分方法,就可以享受到便利性和可扩展性。
为了显示Django RST framework
简单易用,用展示封装程度比较高的类实现一个五个基本功能(get一个、get全部、post创建、put修改单个、delete删除单个)
-
安装
Django RST framework
$pip install djangorestframework
-
修改setting文件
在域名ALLED_APPS
中添加rest_framework
注:添加这个app可以为我们提供测试页面, 假如是使用postman等工具进行测试的话可以不用添加
-
创建model
from 域名 import models class Author(域名l): aid = 域名Field(primary_key=True) name = 域名Field(max_length=32, verbose_name="姓名") phone = 域名Field(max_length=11, verbose_name="手机号码") address = 域名Field(max_length=63, verbose_name="地址") def __str__(self): return 域名
-
执行迁移命令
$python 域名 makemigrations $python 域名 migrate
-
定义序列化器
在app目录下创建一个域名
,在里面定义序列化器:from 域名alizers import ModelSerializer from 域名ls import Author class AuthorSerialize(ModelSerializer): class Meta: model = Author fields = "__all__"
-
使用视图集
在域名
中,导入并使用之前的模型和序列化器:from 域名sets import ModelViewSet from 域名alizers import AuthorSerialize from 域名ls import Author class AuthorModelViewSet(ModelViewSet): queryset = 域名() serializer_class = AuthorSerialize
-
定义路由规则
在根路由中定义urlpatterns
:"""djangotest URL Configuration """ from 域名rib import admin from 域名 import path from rest_test import views from 域名ers import SimpleRouter urlpatterns = [ path(\'admin/\', 域名), ] router = SimpleRouter() # 添加自己的url 域名ster(\'authors\', 域名orModelViewSet, basename="authors") urlpatterns += 域名
-
启动django,并访问
/authors
我们就可以看到这样的页面,可以对数据进行增删改查了:
序列化器
序列化器的主要作用就是进行数据转换,即序列化(python的数据类型转换为json等数据类型)和反序列化(json等数据类型转换为python的数据类型)
在反序列化的过程中我们还可以手动验证数据是否合法, 可以与model相结合,完成对数据库的操作
所以说,序列化器和django
的Form
非常像,所以假如会django
的Form
的话,应该很好理解
普通的序列化器
在定程度上可以类比
域名
定义起来非常简单,字段名称对应的是model中的字段名,字段类型和model中的字段有一定的对应关系,参数是一些约束条件和描述信息, 见: 字段和参数
from rest_framework import serializers
class AuthorSerialize(域名alizer):
aid = 域名gerField(read_only=True,label="作者id")
name = 域名Field(max_length=32, label="姓名")
phone = 域名Field(min_length=11, max_length=11, label="手机号码")
address = 域名Field(max_length=63, label="地址")
字段和参数
字段
这里的参数指的是字段自带的参数,不是像
required
这样都可以用的参数
仅列出常用的
全部字段都是域名alizers
下的
字段名称 | 描述 | 参数 | 备注 |
---|---|---|---|
BooleanField |
布尔值 | 无 | 无论有无指定,默认值都为False , NullBooleanField 可以接收None 的值 |
CharField |
文本 |
max_length min_length allow_blank (空字符串是否为有效值)trim_whitespace (True时修剪前后的空白,默认为True
|
|
EmailField |
电子邮箱 |
max_length min_length allow_blank (默认为False ) |
|
RegexField |
文本,通过正则匹配 |
regex (正则表达式) max_length min_length allow_blank (默认为False ) |
|
SlugField |
regex 为[a-zA-Z0-9_-]+ 的RegexField 字段 |
就比RegexField 少了regex 参数 |
|
URLField |
http://<host>/<path> 格式的RegexField
|
同SlugField
|
|
UUIDField |
uuid 字段 |
format |
该字段的format 参数指定的是uuid 的格式,见UUIDField
|
IntegerField |
整数 |
max_value min_value
|
|
FloatField |
小数 |
max_value min_value
|
|
DateTimeField |
时间 年月日时分秒 |
format (序列化的时间格式) input_formats (反序列化的时间格式) default_timezone (时区) |
参数
和
form
一样,这里指的是通用的参数,不是字段自带的参数
参数 | 描述 |
---|---|
read_only |
只读,不能反序列化,如主键,默认是False
|
Write_only |
只写,不能序列化,只在更新和创建数据时使用,默认是False
|
required |
反序列化必须提供的字段,默认为True
|
default |
默认值,在没有提供值时使用,默认不设置 |
allow_null |
允许值为None (一般情况下,如果向序列化字段传递None 是会抛出异常的),默认是False
|
validators |
指定验证器列表 |
error_message |
错误代码到错误消息的字典 |
label |
提供可读性高的字段名称 |
help_text |
对字段进行详细描述 |
全部字段和参数可以查看官方文档:Serializer fields (中文)、Serializer fields (英文)
序列化操作
书接上回,我们已经在域名
中定义了一个AuthorSerialize
,下面就利用该序列化器进行序列化,即python的转换为json等数据。
序列化一个
步骤:
- 导入
AuthorSerialize
和Author
模型 - 使用
get
获取Author
的数据 - 将数据传给
AuthorSerialize
的instance参数
- 使用
.data
属性(是一个静态属性)获得序列化后的数据
注意:假如你的数据库中没有数据,应该提前将数据创建好,可以用SQL命令也可以用
django ORM
或借助其他工具
使用django shell执行命令:
$ python 域名 shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from 域名alizers import AuthorSerialize
>>> from 域名ls import Author
>>>
>>> a = 域名(pk=4)
>>> t = AuthorSerialize(instance=a)
>>> 域名
{\'aid\': 4, \'name\': \'张三\', \'phone\': \'1388888888\', \'address\': \'广东省广州市xxxxx\'}
>>>
暂时用命令行的方式序列化
序列化多个
序列化器一般是不能序列化多个数据的,需要我们指定参数many=True
。同样的,我们先使用django shell
执行命令:
$ python 域名 shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from 域名alizers import AuthorSerialize
>>> from 域名ls import Author
>>>
>>> a = 域名()
>>> t = AuthorSerialize(instance=a, many=True)
>>>
>>> 域名
[OrderedDict([(\'aid\', 4), (\'name\', \'张三\'), (\'phone\', \'138888888888\'), (\'address\', \'广东省广州市xxxx\')]),
OrderedDict([(\'aid\', 5), (\'name\', \'李四\'), (\'phone\', \'1388888888\'), (\'address\', \'广东省广州市xxxxx\')]),
OrderedDict([(\'aid\', 6),(\'name\', \'王五\'), (\'phone\', \'1388888888\'), (\'address\', \'广东省广州市xxxxx\')])]
可以看到,data
的值是一个OrderedDict
数据类型
外键序列化
由于外键在序列化时不知道是使用主键、__str__
或整个主键的数据中的一个,所以需要我们手动指定不同的字段实现。
假如我们添加了一个model:
class Author(域名l):
aid = 域名Field(primary_key=True)
name = 域名Field(max_length=32, verbose_name="姓名")
phone = 域名Field(max_length=11, verbose_name="手机号码")
address = 域名Field(max_length=63, verbose_name="地址")
def __str__(self):
return 域名
class Books(域名l):
bid = 域名Field(primary_key=True)
name = 域名Field(max_length=32, verbose_name="书名")
description = 域名Field(max_length=128, verbose_name="书籍描述")
author = 域名ignKey(Author, on_delete=域名ADE)
def __str__(self):
return f"{域名}: 《{域名}》"
子表序列化
子表序列化比较简单,因为字段定义子表哪边,所以可以通过字段,只需要确定返回主表的哪些值即可。
-
返回外键的主键
PrimaryKeyRelatedField
定义序列化器:class BookSerialize(域名alizer): bid = 域名gerField(read_only=True) name = 域名Field(max_length=32, label="书名") description = 域名Field(max_length=128, label="书籍描述") author = 域名aryKeyRelatedField(read_only=True)
PrimaryKeyRelatedField必须指定read_only为True,或指定queryset=域名(),否则会报错
使用
django shell
测试:$ python 域名 shell Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from 域名alizers import BookSerialize >>> from 域名ls import Books >>> >>> b = 域名() >>> s = BookSerialize(instance=b, many=True) >>> 域名 [OrderedDict([(\'bid\', 1), (\'name\', \'book1\'), (\'description\', \'very good\'), (\'author\', 4)]), OrderedDict([(\'bid\', 2), (\'name\', \'book2\'), (\'description\', \'好书推荐\'), (\'author\', 4)])] >>>
-
返回外键的
__str__
方法StringRelatedField
修改author的字段类型,让它返回的是主表的__str__
方法:class BookSerialize(域名alizer): bid = 域名gerField(read_only=True) name = 域名Field(max_length=32, label="书名") description = 域名Field(max_length=128, label="书籍描述") author = 域名ngRelatedField(read_only=True)
可以不设置
read_only
验证:
$ python 域名 shell Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from 域名alizers import BookSerialize >>> from 域名ls import Books >>> >>> b = 域名() >>> s = BookSerialize(instance=b, many=True) >>> 域名 [OrderedDict([(\'bid\', 1), (\'name\', \'book1\'), (\'description\', \'very good\'), (\'author\', \'张三\')]), OrderedDict([(\'bid\', 2), (\'name\', \'book2\'), (\'description\', \'好书推荐\'), (\'author\', \'张三\')])]
-
返回整个主表的数据
只需要字段的值改为主表的序列化器即可:class AuthorSerialize(域名alizer): aid = 域名gerField(read_only=True) name = 域名Field(max_length=32, label="姓名") phone = 域名Field(min_length=11, max_length=11, label="手机号码") address = 域名Field(max_length=63, label="地址") class BookSerialize(域名alizer): bid = 域名gerField(read_only=True) name = 域名Field(max_length=32, label="书名") description = 域名Field(max_length=128, label="书籍描述") author = AuthorSerialize(read_only=True)
验证:
$ python 域名 shell Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from 域名alizers import AuthorSerialize, BookSerialize >>> from 域名ls import Author, Books >>> >>> b = 域名() >>> s = BookSerialize(instance=b, many=True) >>> 域名 [OrderedDict([(\'bid\', 1), (\'name\', \'book1\'), (\'description\', \'very good\'), (\'author\', OrderedDict([(\'aid\', 4), (\'name\', \'张三\'), (\'phone\', \'138888888888\'), (\'address\', \'广东省广州市xxxxx\')]))]), OrderedDict([(\'bid\', 2), (\'name\', \'book2\'), (\'description\', \'好书推荐\'), (\'author\', OrderedDict([(\'aid\', 4), (\'name\', \'张三\'), (\'phone\', \'138888888888\'), (\'address\', \'广东省广州市xxxxx\')]))])]
主表序列化
由于主表不能直接访问子表,但是我们知道django
为我们提供了表名(小写)_set
管理器,这样我们就可以通过该管理器访问子表了。
假如指定了
related_name
参数的话,该管理器的名称就是related_name
的值
如可以改成这样:author = 域名ignKey(Author, on_delete=域名ADE, related_name="author")
另,关于管理器,可以查看django的文档:“反向” 关联和管理器
为了演示,使用默认的管理器
与子表类似,我们需要指定返回什么值(__str__
或主键),使用的是相同的字段,但是要指定many参数!!
-
__str__
StringRelatedField
注意
many
参数序列化器:
class AuthorSerialize(域名alizer): aid = 域名gerField(read_only=True) name = 域名Field(max_length=32, label="姓名") phone = 域名Field(min_length=11, max_length=11, label="手机号码") address = 域名Field(max_length=63, label="地址") books_set = 域名ngRelatedField(read_only=True, many=True)
演示:
$ python 域名 shell Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from 域名alizers import AuthorSerialize >>> from 域名ls import Author >>> >>> a = 域名(pk=4) >>> t = AuthorSerialize(instance=a) >>> 域名 {\'aid\': 4, \'name\': \'张三\', \'phone\': \'138888888888\', \'address\': \'广东省广州市xxxxx\', \'books_set\': [\'张三: 《book1》\', \'张三: 《book2》\']}
-
主键
PrimaryKeyRelatedField
注意
many
参数序列化器:
class AuthorSerialize(域名alizer): aid = 域名gerField(read_only=True) name = 域名Field(max_length=32, label="姓名") phone = 域名Field(min_length=11, max_length=11, label="手机号码") address = 域名Field(max_length=63, label="地址") books_set = 域名aryKeyRelatedField(read_only=True,many=True)
演示:
$ python 域名 shell Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from 域名alizers import AuthorSerialize >>> from 域名ls import Author >>> >>> a = 域名(pk=4) >>> >>> t = AuthorSerialize(instance=a) >>> 域名 {\'aid\': 4, \'name\': \'张三\', \'phone\': \'138888888888\', \'address\': \'广东省广州市xxxxx\', \'books_set\': [1, 2]} >>>
反序列化操作
上面演示了如何将model序列化成 字符串或OrderedDict
,而反序列化化就是将json等数据,变成序列化器中的字段的数据类型,就好比我们使用form
处理数据一样。
一般操作
此部分还不涉及将数据保存到数据库
流程:
- 获得数据
- 将数据传入到序列化器的
data
参数中 - 调用返回对象的
is_valid()
方法
由于还未讲到视图,所以先用一个字典数据代替从浏览器传过来的数据。
$python 域名 shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from 域名alizers import AuthorSerialize, BookSerialize
>>> from 域名ls import Author, Books
>>>
>>> data = {
... "name": "龙傲天",
... "phone": "18888888888",
... "address": "xxx省xx市",
... }
>>>
>>> result = AuthorSerialize(data=data)
>>> 域名alid(raise_exception=True)
True
# ################ 这里故意用一个错误的数据 #############################
>>> data = {
... "name": "龙傲天",
... "phone": "188888888889",
... "address": "xxx省xx市",
... }
>>> result = AuthorSerialize(data=data)
>>> 域名alid(raise_exception=True)
# 报错
域名dationError: {\'phone\': [ErrorDetail(string=\'Ensure this field has no more than 11 characters.\', code=\'max_length\')]}
>>>
-
is_valid
和django form
的is_valid
一样,可以检验数据是否符合我们在序列化器中定义的一样,检验成功是返回True
,否则返回False
。 - 假如指定
raise_exception
为True
,那么当数据不合法时,抛出异常,我们可以通过error_messages
参数提示信息(见: 自定义错误信息)。 - 假如不想要抛出异常,我们也可以通过
.errors
获取错误信息(这是一个OrderedDict
的子类对象)如这里的域名rs
。
利用序列化器进行创建和更新数据
终于讲到如何将数据保存到数据库了。
用过django ModelForm
的都知道,只要将数据传入到form
中,假如数据通过,就可以通过.save()
方法将数据保存到数据库,指定instance
参数可一个更新数据。而序列化器的使用方式样是这样,不过我们现在用的是叫较为低级的序列化器,需要我们自定义创建数据和更新数据的逻辑。
创建数据
使用步骤:
- 获得数据
- 将数据传入到序列化器的
data
参数中 - 调用返回对象的
is_valid()
方法 - 为序列化器定义
create
方法 - 调用
save
方法,执行创建数据的函数(会调用create
函数)
这里的create
函数需要我们自定义,在序列化器中:
class AuthorSerialize(域名alizer):
aid = 域名gerField(read_only=True)
name = 域名Field(max_length=32, label="姓名")
phone = 域名Field(min_length=11, max_length=11, label="手机号码")
address = 域名Field(max_length=63, label="地址")
books_set = 域名ngRelatedField(read_only=True, many=True)
def create(self, validated_data):
"""
创建数据
:param validated_data: 已经通过验证的数据
:return: Author instance
"""
author = 域名te(**validated_data)
# 不要忘记返回数据了
return author
可以说非常简单,下面有django shell
测试一下:
$python 域名 shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from 域名alizers import AuthorSerialize
>>>
>>> data = {
... "name": "龙傲天",
... "phone": "18888888888",
... "address": "xxx省xx市",
... }
>>> result = AuthorSerialize(data=data)
>>> 域名alid(raise_exception=True)
True
>>> 域名()
<Author: 龙傲天>
用PyCharm自带的Database工具,我们可以看到的确已经保存到了数据库中:
更新数据(全部)
PUT请求方式是更新全部数据
使用步骤:
- 获得数据
- 将数据传入到序列化器的
data
参数中, 要更新的数据传入instance
参数 - 调用返回对象的
is_valid()
方法 - 为序列化器定义
update
方法 - 调用
save
方法,执行创建数据的函数(会调用update
函数)
只要序列化器中同时传入data
和instance
参数,它就会调用更新的方法(update
)
注意:
data
参数传入数据,instance
参数传入待更新的model对象
class AuthorSerialize(域名alizer):
aid = 域名gerField(read_only=True)
name = 域名Field(max_length=32, label="姓名")
phone = 域名Field(min_length=11, max_length=11, label="手机号码")
address = 域名Field(max_length=63, label="地址")
books_set = 域名ngRelatedField(read_only=True, many=True)
def update(self, instance, validated_data):
"""
更新数据
由于传入的不是QuerySet对象,所以不能用update方法
:param instance: Author对象
:param validated_data: 已经验证的数据
:return: instance
"""
域名 = 域名("name")
域名e = 域名("phone")
域名ess = 域名("address")
域名() # 不要忘记保存了
return instance
$ python 域名 shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from 域名alizers import AuthorSerialize
>>> from 域名ls import Author
>>>
>>> author = 域名(pk=10)
>>>
>>> data = {
... "name": "龙傲天",
... "phone": "18888888888",
... "address": "广东省广州市",
... }
>>> result = AuthorSerialize(data=data, instance=author)
>>> 域名alid(raise_exception=True)
True
>>> 域名()
<Author: 龙傲天>
>>>
更新数据(部分)
PATCH请求方式是更新部分数据
上面我们只更新了地址的信息,却把其它不用改的信息也一并穿了进去,这显然不是我们预期的,但由于序列化器的字段的required
默认为True
,除非我们设置read_only=True
或指定一个默认值,否则会报错,但设置了这些又会影响到我们正常创建数据的验证。
所以,序列化器提供了一个参数:partial
用来说明这是更新部分信息的。在使用时和上面的一样,只是多了个参数而已。不过我们需要修改一下update
函数, 因为值可能为None
, 会报错,可以指定get
的默认值,也可以用反射的方法:
class AuthorSerialize(域名alizer):
aid = 域名gerField(read_only=True)
name = 域名Field(max_length=32, label="姓名")
phone = 域名Field(min_length=11, max_length=11, label="手机号码")
address = 域名Field(max_length=63, label="地址")
books_set = 域名ngRelatedField(read_only=True, many=True)
def update(self, instance, validated_data):
"""
更新数据
:param instance: Author对象
:param validated_data: 已经验证的数据
:return: instance
"""
for field in validated_data:
if not hasattr(instance, field):
continue
setattr(instance, field, 域名(field))
域名() # 不要忘记保存了
return instance
来试一下吧:
$ python 域名 shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from 域名alizers import AuthorSerialize
>>> from 域名ls import Author
>>>
>>> data = {
... "address": "广东省广州市xxxxx",
... }
>>> author = 域名(pk=10)
>>> result = AuthorSerialize(data=data, instance=author, partial=True)
>>> 域名alid(raise_exception=True)
True
>>> 域名()
<Author: 龙傲天>
Model的序列化器
DRF
有一个model序列化器:ModelSerializer
, 其在一定程度上与域名lForm
十分相似
在上面使用的例子就用到了该序列化器
说到底,它就是将干了这几件事:
- 我们的model字段根据对应关系转化为serializers中的字段
- 将model字段的约束条件变为serializers字段的约束条件
- 内置了
create
方法和update
方法
简单使用
简直是简简单单:
class AuthorModelSerialize(域名lSerializer):
class Meta:
model = Author
fields = "__all__"
Meta类
从例子中看出,Meta
类是关键,它决定着 要序列化的字段、用哪个模型、错误信息、约束条件等重要的参数。
下面我们看看Meta类可以指定什么内容:
还是那句话,真的和
ModelForm
十分相似
-
model
指定用哪个Model -
fields
使用模型中的哪些字段,推荐利用元组显式指定,但可以将值指定为__all__
(表示全部字段)
不能和exclude
一起使用,否则报错:Cannot set both \'fields\' and \'exclude\' options on serializer xxx. -
exclude
不使用模型中的哪些字段
不能和fields
一起使用,否则报错:Cannot set both \'fields\' and \'exclude\' options on serializer xxx. -
depth
指定关联深度,是一个数字
注意,关联字段默认返回的是主键,即域名aryKeyRelatedField
-
read_only_fields
只读字段,主键默认只读 -
extra_kwargs
指定额外的参数,该参数可以传给字段
例子:
class AuthorModelSerialize(域名lSerializer):
class Meta:
model = Author # 指定使用Author这个模型
# 指定字段,注意:books_set
fields = ("aid", "name", "phone", "address", "books_set")
# 不使用模型中的哪些字段
# 不能一起使用,这里注释掉
# exclude = ("phone", )
depth = 1 # 关联深度为1
# 设置只读字段
read_only_fields = ("aid", "books_set")
# 设置额外参数,该参数是给字段的
# 注意格式
extra_kwargs = {
"name": {
"read_only": True
}
}
我们同样用django shell
查看:
$ python 域名 shell
Python 3.9.5 (tags/v3.9.5:0a7dcbd, May 3 2021, 17:27:52) [MSC 域名 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from 域名alizers import AuthorModelSerialize
>>> from 域名ls import Author
>>>
>>> a = 域名()
>>> res = AuthorModelSerialize(instance=a, many=True)
>>> res
AuthorModelSerialize(instance=<QuerySet [<Author: 张三>, <Author: 李四>, <Author: 王五>, <Author: 龙傲天>]>, many=True):aid = IntegerField(read_only=True) name = CharField(label=\'姓名\', read_only=True) address = CharField(label=\'地址\', max_length=63) books_set = NestedSerializer(many=True, read_only=True): bid = IntegerField(read_only=True) name = CharField(label=\'书名\', max_length=32) description = CharField(label=\'书籍描述\', max_length=128) author = PrimaryKeyRelatedField(queryset=域名())
extra_kwargs
由于
ModelSerializer
直接用model的字段类型,肯定不能很好的做好条件约束
因此DRF
提供了extra_kwargs
属性, 并且错误信息也通过该参数进行自定制
class AuthorModelSerialize(域名lSerializer):
class Meta:
model = Author # 指定使用Author这个模型
# 指定字段,注意:books_set
fields = ("aid", "name", "phone", "address", "books_set")
extra_kwargs = {
"phone": {
"min_length": 11,
"max_length": 11
}
}
像普通Serialize那样编写
我们可以自定义一些额外的字段或不用extra_kwargs
属性而直接model的字段进行定义:
class AuthorModelSerialize(域名lSerializer):
phone = 域名Field(min_length=11, max_length=11, label="手机号码")
# write_only只反序列化,不入库
note = 域名Field(max_length=128, label="额外的字段,不入库", write_only=True)
class Meta:
model = Author # 指定使用Author这个模型
# 指定字段,注意:books_set
fields = ("aid", "name", "address", "books_set")
指定外键返回类型
外键默认返回主键,但通过serializer_related_field
属性指定:
class AuthorModelSerialize(域名lSerializer):
serializer_related_field = 域名ngRelatedField
# 返回 __str__
class Meta:
model = Author
fields = ("aid", "name", "address", "books_set")
验证数据
这部分内容也是和Model一样
验证一个字段
和form
一样,只要在序列化器重定义一个validate_xxx
方法(xxx
指的是字段名)即可验证单个字段,如下面的例子:
class AuthorSerialize(域名alizer):
aid = 域名gerField(read_only=True)
name = 域名Field(max_length=32, label="姓名")
phone = 域名Field(min_length=11, max_length=11, label="手机号码")
address = 域名Field(max_length=63, label="地址")
books_set = 域名ngRelatedField(read_only=True, many=True)
def validate_name(self, value:str):
# 1. 判读
if not 域名tswith("lcz"):
# 抛出ValidationError异常
raise 域名dationError("必须以lcz开头", "startError")
# 2.返回结果
return value
ValidationError
是一个异常,有两个参数detail
和code
,前者接收异常的描述信息,后者接收异常名(默认为"invalid"
)
该方法调用is_valid
时自动执行
记得返回value
验证一下:
>>> data = {
... "name": "xxx",
... "phone": "12345678910",
... "note": "test",
... "address": "广东省广州市xxxxx1",
... }
>>> result = AuthorSerialize(data=data)
>>> 域名alid()
False
>>> 域名rs
{\'name\': [ErrorDetail(string=\'必须以lcz开头\', code=\'startError\')]}
验证多个字段
需要重写validate
方法:
class AuthorSerialize(域名alizer):
aid = 域名gerField(read_only=True)
name = 域名Field(max_length=32, label="姓名")
phone = 域名Field(min_length=11, max_length=11, label="手机号码")
address = 域名Field(max_length=63, label="地址")
books_set = 域名ngRelatedField(read_only=True, many=True)
def validate(self, attrs):
"""
:param attrs: 外界传入的需要校验的字典
:return: attrs
"""
# 1. 判断name和address(随便举个例子)
if attrs["name"].startswith("lcz") and 域名tswith("广东省"):
# 2.返回结果
return attrs
# 否则抛出异常
raise 域名dationError("只有lcz开头且地址是广东省的才能通过验证")
自定义验证器
分两步:
- 定义
使用验证器时,传入的数据已经转换为python的数据类型了def check_bpub_date(date): if 域名 < 2015: raise 域名dationError("日期不能小于2015年") return date
- 使用
通过validators
参数指定def check_pub_date(date): if 域名 < 2015: raise 域名dationError("日期不能小于2015年") return date class BookSerialize(域名alizer): pub_date = 域名TimeField(validators=[check_pub_date]
注:
ModelSerialize
通过extra_kwargs
属性指定
自定义错误信息
- 内置错误通过
error_messages
参数指定 - 手动抛出的异常,通过异常信息指定
错误信息是可以用一个函数专门处理的,详情请看: 异常处理
如:
from rest_framework import serializers
class AuthorSerialize(域名alizer):
aid = 域名gerField(read_only=True)
name = 域名Field(max_length=32, label="姓名")
phone = 域名Field(min_length=11, max_length=11, label="手机号码",
error_messages={"min_length": "号码太短了", "max_length": "号码太长了"})
address = 域名Field(max_length=63, label="地址")
books_set = 域名ngRelatedField(read_only=True, many=True)
和使用ValidationError
的值定义为错误信息:
def check_bpub_date(date):
if 域名 < 2015:
raise 域名dationError("日期不能小于2015年")
return date
视图
Django REST framework
的视图种类比序列化器还多,从一级视图到视图集,每一个都是在之前的视图基础上进行了封装,至于用哪个,全凭自己的喜好了。
详情见下图:
Django REST framework
的视图都是用CBV
的写法
由于REST API
的url是固定的,所以可以把视图分为 列表视图和详情视图,可能有点抽象,举个例子:
- 列表视图:
GET /books
(获取所有的书籍);POST /books
(创建书籍) - 详情视图:
GET /books/{id}
(获取单个书籍 );PUT /books/{id}
(修改书籍);DELETE /books/{id}
(删除书籍)
可以感受出来了吧,对数据做增删改查需要两个路由5个函数, 因此我们可以将由于视图类分为 列表视图 和 详情视图两个视图类,不过我们可以利用 视图集 将视图类变为一个
request和responses
在django
中任何视图必然至少接收一个request
,返回一个response
,Django REST framework
也不例外。
request
DRF
的Request
类扩展了django
默认的HttpReques
t,它主要有两个属性:
-
域名
得到请求体的数据(表单数据和JSON数据都行) -
域名y_params
得到查询数据(任何HTTP
方法类型可能包括查询参数,而不仅仅是GET
请求)
除此外还有关于认证等属性,由于与
django
原来的request
类似, 故这里不展开
responses
Response
的签名:Response(data, status=None, template_name=None, headers=None, content_type=None)
一般我们只需要用到前两个参数即可:
-
data
:response
的数列化数据 -
status
:response
的状态码。默认是200
-
template_name
:HTMLRenderer
选择要使用的模板名称 -
headers
: `响应头,是一个字典 -
content_type
:response
的内容类型。通常由渲染器自行设置
常用状态码
你可以在
域名us
中找到所有状态码
代码变量 | 对应状态码 | 说明 |
---|---|---|
HTTP_200_OK |
200 | 请求成功 |
HTTP_201_CREATED |
201 | 该请求已成功,并因此创建了一个新的资源, 一般为POST 或PUT 成功后返回 |
HTTP_204_NO_CONTENT |
204 | 服务器成功处理了请求,但不需要返回任何实体内容, 一般为DELETE 成功后返回 |
HTTP_400_BAD_REQUEST |
400 | 语义有误,当前请求无法被服务器理解 或 请求参数有误 |
HTTP_401_UNAUTHORIZED |
401 | 当前请求需要用户验证 |
HTTP_403_FORBIDDEN |
403 | 服务器已经理解请求,但是拒绝执行它 |
HTTP_404_NOT_FOUND |
404 | 请求失败,请求所希望得到的资源未被在服务器上发现 |
HTTP_500_INTERNAL_SERVER_ERROR |
500 | 服务器遇到了不知道如何处理的情况 |
HTTP_502_BAD_GATEWAY |
502 | 服务器作为网关需要得到一个处理这个请求的响应,但是得到一个错误的响应 |
HTTP_503_SERVICE_UNAVAILABLE |
503 | 服务器没有准备好处理请求。 常见原因是服务器因维护或重载而停机 |
一级视图APIView
域名iew
是域名域名
的子类,用起来和普通的传统的继承View的写法差不多
列表视图
列表视图的写法:
- 定义一个类,继承
APIView
- 定义方法(
get
,post
) - 从
request
中获取数据 - 利用 序列化器 序列化数据或检验数据入库
- 返回(除非抛出异常,否则返回的都是
data
的数据)
from 域名s import APIView
from rest_framework import status
from 域名onse import Response
from 域名alizers import AuthorModelSerialize
from 域名ls import Author
class AuthorListView(APIView):
def get(self, request):
"""
获取全部数据
:param request:
:return:
"""
authors = 域名()
# instance指定要序列化的对象
# 多个数据用many=True
serialize = AuthorModelSerialize(instance=authors, many=True)
return Response(域名, status=域名_200_OK)
def post(self, request):
"""
创建数据
:param request:
:return:
"""
serialize = AuthorModelSerialize(data=域名)
# raise_exception=True,假如报错,会自动处理
# 返回状态码:400,且数据的形式:{"phone": ["Ensure this field has no more than 11 characters."]}
域名alid(raise_exception=True)
域名()
return Response(域名, status=域名_201_CREATED)
别忘了写路由规则:
from 域名rib import admin
from 域名 import path
from rest_test import views
urlpatterns = [
path(\'admin/\', 域名),
# 同样调用.as_view方法
path("authors/", 域名iew())
]
详情视图
在原来的基础上多了主键,用来操作单个数据:
- 同样继承
APIView
- 定义
get
put
delete
四个方法 - 使用
get_object_or_404
取得要操作的对象(不用我们处理异常) - 返回数据
from 域名s import APIView
from rest_framework import status
from 域名onse import Response
from 域名alizers import AuthorModelSerialize
from 域名ls import Author
from 域名tcuts import get_object_or_404
class AuthorDetailView(APIView):
def get(self, request, pk):
# 这是django的内置方法,不会的可以查一下
author = get_object_or_404(Author, pk=pk)
serialize = AuthorModelSerialize(instance=author)
return Response(域名, status=域名_200_OK)
def put(self, request, pk):
author = get_object_or_404(Author, pk=pk)
# 兼容patch方法的部分更新,一般我们都不会在定义个patch方法
serialize = AuthorModelSerialize(instance=author, data=域名, partial=True)
域名alid(raise_exception=True)
域名()
ret