DRF基础笔记

1. RESTful API规范

1.1 域名(domain)

# 应该尽量将API部署在专用域名之下。
https://api.example.com
https://www.example.com

1.2 版本(version)

# 方法一: 版本号放入 URL
http://api.example.com/app/1.0/foo
http://api.example.com/app/2.0/foo
http://api.example.com/app/3.0/foo

# 方法二: 版本号放入 HTTP 头信息中

Accept: version=1.0
Accept: version=2.0
Accept: version=3.0

1.3 路径(Endpoint)

路径又称"终点"(endpoint),表示API的具体网址,每个网址代表一种资源(resource) 对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。

对于一个简洁结构,你应该始终用名词。 此外,利用的HTTP方法可以分离网址中的资源名称的操作。

1.4 HTTP动词

对于资源的具体操作类型,由HTTP动词表示。

常用的HTTP动词有下面四个(括号里是对应的SQL命令)。

  • GET(SELECT):从服务器取出资源(一项或多项)。

  • POST(CREATE):在服务器新建一个资源。

  • PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。

  • DELETE(DELETE):从服务器删除资源。

还有三个不常用的HTTP动词。

  • PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。

  • HEAD:获取资源的元数据。

  • OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。 下面是一些例子。

1.5 过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。 下面是一些常见的参数。query_string 查询字符串,地址栏后面问号后面的数据,格式: name=xx&sss=xxx

1.6 状态码(status Codes)

服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。

状态码的完全列表参见这里或这里。 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html

1.7 错误处理(Error handling)

如果状态码是4xx,服务器就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。

1.8 返回结果

针对不同操作,服务器向用户返回的结果应该符合以下规范。

1.9 超媒体(Hypermedia API)

RESTful API最好做到Hypermedia(即返回结果中提供链接,连向其他API方法),使得用户不查文档,也知道下一步应该做什么。

比如,Github的API就是这种设计,访问api.github.com会得到一个所有可用API的网址列表。

从上面可以看到,如果想获取当前用户的信息,应该去访问api.github.com/user,然后就得到了下面结果。

上面代码表示,服务器给出了提示信息,以及文档的网址。

1.10 其他

服务器返回的数据格式,应该尽量使用JSON,避免使用XML

2. Django Rest_Framework简介与安装

2.1 Django Rest_Framework简介

核心思想: 缩减编写api接口的代 – DRF

Django REST framework是一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用。在REST framework中,提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持。REST framework提供了一个API 的Web可视化界面来方便查看测试接口。

  • 中文文档:https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework

  • github: https://github.com/encode/django-rest-framework/tree/master

  • 英文文档:https://www.django-rest-framework.org/

2.2 环境安装与配置

DRF需要以下依赖:

  • Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6)

  • Django (1.10, 1.11, 2.0)

安装 DRF

3. DRF创建项目流程

3.1 添加rest_framework应用

在settings.py的INSTALLED_APPS中添加’rest_framework’。

项目中如果使用rest_framework框架实现API接口,主要有以下三个步骤:

  • 将请求的数据(如JSON格式)转换为模型类对象

  • 操作数据库

  • 将模型类对象转换为响应的数据(如JSON格式)

3.2 创建模型操作类

创建数据库(嫌麻烦,可以使用默认的sqlite3测试)

主引用中__init__.py设置使用pymysql作为数据库驱动

settings.py配置文件中设置mysql的账号密码

终端下,执行数据迁移

3.3 创建序列化器

在students应用目录中新建 serializers.py 用于保存该应用的序列化器。 创建一个 StudentModelSerializer 用于序列化与反序列化。

  • model 指明该序列化器处理的数据字段从模型类Student参考生成

  • fields 指明该序列化器包含模型类中的哪些字段,'all’指明包含所有字段

3.4 写视图

在students应用的views.py中创建视图StudentViewSet,这是一个视图集合

  • queryset 指明该视图集在查询数据时使用的查询集

  • serializer_class 指明该视图在进行序列化或反序列化时使用的序列化器

3.5 定义路由

在students应用的urls.py中定义路由信息。

最后把students子应用中的路由文件加载到总路由文件中.

3.6 运行测试

运行该django项目

在浏览器输入路径,可以看到DRF提供的API Web浏览页面 在页面底部可以提交post请求,添加到数据库 在网址中输入路径/1,可以访问id为1的信息 在网址中输入路径/S/,可以访问删除的接口

4. 序列化器–Serializer

作用:

  1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串

  2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型

  3. 反序列化,完成数据校验功能

定义好Serializer类后,就可以创建Serializer对象了。

4.1 Serializer构造方法

说明:

1)用于序列化时,将模型类对象传入instance参数

2)用于反序列化时,将要被反序列化的数据传入data参数

3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

通过context参数附加的数据,可以通过Serializer对象的context属性获取。

  1. 使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以。

  2. 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。

  3. 序列化器的字段声明类似于我们前面使用过的表单系统。

  4. 开发restful api时,序列化器会帮我们把模型数据转换成字典.

  5. drf提供的视图会帮我们把字典转换成json,或者把客户端发送过来的数据转换字典.

4.2 序列化功能的使用

1 定义一个序列化器,在应用中创建一个py文件,serializers.py

2 写views.py文件

3 配置路由

测试结果:

4.3 反序列化功能的使用

1 定义一个序列化器,在当前应用中新建serializers.py

2 写views.py文件

结合 全局钩子和局部钩子 验证

3 url:

Tip: is_valid中的raise_exception参数

4.4 反序列化功能校验顺序

执行顺序:

​  1.先执行name中CharField参数中的规则

  2.执行name的局部钩子函数

  3.执行age的IntegerField参数中的规则

  4.执行age的局部钩子函数

  5.执行全局钩子函数

4.5 序列化器一些常用参数的说明

常用字段:

字段
字段构造方式

BooleanField

BooleanField()

NullBooleanField

NullBooleanField()

CharField

CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)

EmailField

EmailField(max_length=None, min_length=None, allow_blank=False)

RegexField

RegexField(regex, max_length=None, min_length=None, allow_blank=False)

SlugField

SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+

URLField

URLField(max_length=200, min_length=None, allow_blank=False)

UUIDField

UUIDField(format='hex_verbose') format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 微软时间戳,通过微秒生成一个随机字符串

IPAddressField

IPAddressField(protocol='both', unpack_ipv4=False, **options)

IntegerField

IntegerField(max_value=None, min_value=None)

FloatField

FloatField(max_value=None, min_value=None)

DecimalField

DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置

DateTimeField

DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)

DateField

DateField(format=api_settings.DATE_FORMAT, input_formats=None)

TimeField

TimeField(format=api_settings.TIME_FORMAT, input_formats=None)

DurationField

DurationField()

ChoiceField

ChoiceField(choices) choices与Django的用法相同

MultipleChoiceField

MultipleChoiceField(choices)

FileField

FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

ImageField

ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

ListField

ListField(child=, min_length=None, max_length=None)

DictField

DictField(child=)

选项参数

参数名称
作用

max_length

最大长度

min_length

最小长度

allow_blank

是否允许为空

trim_whitespace

是否截断空白字符

max_value

最大值

min_value

最小值

通用参数

参数名称
说明

read_only

表明该字段仅用于序列化输出,默认False

write_only

表明该字段仅用于反序列化输入,默认False

required

表明该字段在反序列化时必须输入,默认True

default

反序列化时使用的默认值

allow_null

表明该字段是否允许传入None,默认False

validators

该字段使用的验证器

error_messages

包含错误编号与错误信息的字典

label

用于HTML展示API页面时,显示的字段名称

help_text

用于HTML展示API页面时,显示的字段帮助提示信息

4.6 反序列化的数据保存功能

方式一:直接在视图中保存

方式二:视图中通过save方法来触发序列化器类中的create方法

4.7 反序列化的数据更新功能

在序列化器中定义update方法来数据的更新

视图部分

Tip: 如果只更新部分字段

partial参数默认为False,若partial=True则代表只需要校验传入给序列化器的数据(data),其他的字段不校验.适用于部分数据更新

应用场景:

比如在序列化器中加入定义了五个字段。

如果用户在前端提交数据时,必须要将五个字段都提交,否则就会报错。

但是在现实应用场景下,有很多情况是不需要将五个字段全部提交的。比如数据更新

这个时候想要更改,但是序列化器的代码已经成型了,不太好进行修改。 【eg:提交需要提交5个字段,更新提交2个字段】

4.8 Field字段参数:read_only和write_only

read_only和write_only是field字段里面的参数,也是用来对字段做限制的

4.9 ModelSerializer的使用(推荐)

序列化与反序列化

1 定义一个序列化器,继承serializers.ModelSerializer:

2 视图文件

5. 视图

5.1 request对象和response对象

request对象 在get方法中request.GET<=>request.query_params

在post方法中:

当发送的数据格式为…urlencoded类型时,request.POST<=>request.data

当发送过来的是json类型数据时,我们使用request.data属性能够获取到数据,得到的是普通字典类型。(如果是多选数据,不能使用getlist方法获取)

response对象

通过浏览器访问:

注释浏览器API渲染器(得到的就是一个Json对象):

response的方法的相关参数

  • data: 响应的序列化数据。

  • status: 响应的状态代码。默认为200。

  • template_name: 选择 HTMLRenderer 时使用的模板名称。

  • headers: 设置 HTTP header,字典类型。

  • content_type: 响应的内容类型,通常渲染器会根据内容协商的结果自动设置,但有些时候需要手动指定。

  • Response 可以将内置类型转换成字符串。写到data的位置

响应状态码

所有状态码:

5.2 基于 APIView 视图

APIView总结

1.APIView中封装了自己的request和response

2.django:request.GET对应drf中的request.query_params

3.django:request.POST对应drf中的request.data

4.APIView中的response不需要写参数safe=False,ensure_ascii这些东西了,因为这些功能已经封装在response里面了

5.当我们在浏览器上访问一个url,给你回复的是一个界面。如果我们用postman访问url,给你返回的就是json数据。这是因为response中有相关配置导致的。

APIView实现代码

5.3 基于 GenericAPIView 视图

GenerivAPIView继承自APIView方法,主要增加了操作序列化器和数据库查询的方法。

GenericAPIView中的属性和方法

  • serializer_class 指明视图使用的序列化器

  • get_serializer(self, *args, **kwargs) 返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法

  • queryset 指明使用的数据查询集

  • get_queryset(self) 返回视图使用的查询集,主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写

  • get_object(self) 返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用。在视图中可以调用该方法获取详情信息的模型类对象。

  • *get_serializer_class(self) 当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了。

            返回序列化器类,默认返回serializer_class

5.4 基于Mixin个视图扩展类视图

提供了几种后端视图(对数据资源进行增删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。

这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。

换句话说。就是你在视图中的 get post put 等这些方法中不用写里面的代码了,里面的代码相关的操作已经被封装到对应的Mixin中了。

相比于GenericAPIView,Mixin拓展封装了:调用序列器序列化与反序列化的过程

  • ListModelMixin: self.list(request)

  • CreateModelMixin: self.create(request)

  • RetrieveModelMixin:self.retrieve(request,pk)

  • UpdateModelMixin:self.update(request,pk)

  • DestroyModelMixin:self.destroy(request,pk)

5.5 基于GenericAPIView的视图子类 APIView

上面的代码还是过于麻烦,因为既要继承GenericAPIView又要继承Mixin系列的类。

所以将各自的操作封装成自己的APIView类。用哪个继承哪个。

而且连函数都不用写了,在???APIView类中已经有了,所以不用再写了,继承自己的APIView类即可。

5.6 ViewSet

主要解决问题:之前我们在写视图类的时候,获取多条数据和提交数据放在了一个类中。获取单条数据,更新单条数据,删除多条数据放到了一个类中

这是因为多条数据操作时不需要指定pk值,而针对单条数据时,需要指定pk值。也就是说需要知道你要操作哪一条数据

这样我们就很矛盾。我们没法将5个方法放到同一个类中。

而下面的ViewSet就可以解决这个问题,将5个方法放到同一个类中

通过上面代码我们可以看出。**单条数据操作和多条数据被放到了一个类中。**并且方法名也不再局限于必须要使用get post put...这些方法名了。

这是因为在urls.py中的as_view方法做了一个请求方法和对应函数名的指定

而获取单条数据和获取多条数据能够共存在一个类中,是因为他们处于两个url中。虽然都是get请求过去的,但是他们在不同的url中,会执行各自的视图方法

通过下面url,我们就可以发挥出ViewSet的用处了:

5.7 ViewSet + APIView

5.8 GenericViewSet + Mixin

效果和 Viewset + APIView 是相同的

5.9 以上方法改良后

5.10 基于ModelViewSet视图(终极版)

上面的代码,在导入和继承的时候太麻烦了,需要写5个Mixin类

这时出现了ModelViewSet,ModelViewSet同时继承ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin这五个类,这样写起来就更加简单了。

5.11 ReadOnlyViewset(不常用)

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

5.12 添加自定义动作(action装饰器)

在视图集中,除了上述默认的方法动作外,还可以添加自定义动作,进行扩展。

举例,比如做一个登录方法login:

6. 路由:Routers

6.1 如何添加路由数据

如何添加路由数据,并且和视图函数做关联:

上面的代码就成功生成了路由地址[增/删/改/查一条/查多条的功能],但是不会自动帮我们在视图集自定义方法的路由。

所以我们如果也要给自定义方法生成路由,则需要进行action动作的声明。

6.2 在视图集中附加action声明

在视图集中,如果想要让Router自动帮助我们为自定义的动作生成路由信息,需要使用rest_framework.decorators.action装饰器。

以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。

action装饰器可以接收两个参数:

  • methods: 声明该action对应的请求方式,列表传递

  • detail:

    声明该action的路径是否与单一资源对应,及是否是 xxx//action方法名/

    • True 表示路径格式是xxx/<pk>/action方法名/

    • False 表示路径格式是xxx/action方法名/

6.3 DefaultRouter和SimpleRouter的区别(了解)

DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。

比如使用DefaultRouter时你访问一下http://127.0.0.1:8001/router_stu/ 会看到一个页面。

而SimpleRouter会报错,一般都需要有个查看所有接口的页面,所以我们基本都是用的是DefaultRouter

附:APIView思维导图

6.5 附:drf的执行流程图

参考: https://www.cnblogs.com/libolun/p/13861557.html

7. 认证/权限/限流/过滤/排序/分页/异常处理

7.1 认证 Authentication

可以在配置文件中配置全局默认的认证方案

也可以在每个视图中通过设置authentication_classes属性来设置,比如说我们很多接口的数据都是可以让别人获取数据的,

但是有可能有些接口是调用给别人网站的,有可能到时候我们就需要一些单独的认证了

认证失败会有两种可能的返回值:

  • 401 Unauthorized 未认证

  • 403 Permission Denied 权限被禁止

7.2 自定义认证

全局使用,settings配置文件中使用

局部视图中使用

7.3 权限:Permissions

权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。

  • 在执行视图的dispatch()方法前,会先进行视图访问权限的判断

  • 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断

全局设置默认的权限管理类

局部设置实例

  • AllowAny 允许所有用户,未指明时默认为允许所有用户

  • IsAuthenticated 仅通过认证的用户

  • IsAdminUser 仅管理员用户(可以通过admin创建一个用户进行测试)

  • IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。

7.4 自定义权限

如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部

  • .has_permission(self, request, view)

    是否可以访问视图, view表示当前视图对象

  • .has_object_permission(self, request, view, obj)

    是否可以访问数据对象, view表示当前视图, obj为数据对象

例如在当前子应用下,创建一个权限文件permissions.py中声明自定义权限类:

视图函数

7.5 限流:Throttling

可以对接口访问的频次进行限制,以减轻服务器压力。

一般用于付费购买次数,投票等场景使用.

全局配置

DEFAULT_THROTTLE_RATES 可以使用 second, minute, hour 或day来指明周期。

仅局部配置

7.6 可选择的限流类

1) AnonRateThrottle

限制所有匿名未认证用户,使用IP区分用户。

使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次

2)UserRateThrottle

限制认证用户,使用User id 来区分。

使用DEFAULT_THROTTLE_RATES['user'] 来设置频次

3)ScopedRateThrottle (待定...)

限制用户对于每个视图的访问频次,使用ip或user id,先找的用户id,没有设置用户id的话就会使用ip地址。

例如: 全局

ScopedRateThrottle 局部使用示例

7.7 过滤:Filtering

对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。

在配置文件中增加过滤后端的设置:

在视图中添加 filter_fields 属性,指定可以过滤的字段

7.8 排序

  1. 添加 filter_backends 属性(使用OrderingFilter过滤器)

  2. 指定字段,通过 **ordering_fields **属性指定需要按某字段排序

7.9 排序 + 过滤

如果需要在过滤以后再次进行排序,则需要两者结合!

需要使用 DjangoFilterBackend

7.10 分页:Pagination

REST framework提供了分页的支持。

我们可以在配置文件中设置全局的分页方式

也可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_class属性来指明。

注意:如果在视图内关闭分页功能,只需在视图内设置

7.10.1 PageNumberPagination

前端访问网址形式

可以在子类中定义的属性:

  • page_size 每页数目

  • page_query_param 前端发送的页数关键字名,默认为"page"

  • page_size_query_param 前端发送的每页数目关键字名,默认为None

  • max_page_size 前端最多能设置的每页数量

7.10.2 LimitOffsetPagination

前端访问网址形式:其实就是通过偏移量来取数据

可以在子类中定义的属性:

  • default_limit 默认限制,每页数据量大小,默认值与PAGE_SIZE设置一致

  • limit_query_param limit参数名,默认'limit' , 可以通过这个参数来改名字

  • offset_query_param offset参数名,默认'offset' ,可以通过这个参数来改名字

  • max_limit 最大limit限制,默认None, 无限制

7.11 异常处理:Exception

7.11.1 自定义异常的捕获

先看一个简单的实例:

视图:

当索引超出范围后,会抛出错误:

在REST framework中提供了异常处理,我们可以自定义异常处理函数,来捕获异常反馈前端

1 在应用下新建一个文件exceptions.py,

2 在setting文件中配置REST_FRAMEWORK

这样就完成了自定义异常的捕获:

同理,补充上处理关于数据库的异常:

7.11.2 REST framework默认异常

  • APIException 所有异常的父类

  • ParseError 解析错误

  • AuthenticationFailed 认证失败

  • NotAuthenticated 尚未认证

  • PermissionDenied 权限决绝

  • NotFound 未找到

  • MethodNotAllowed 请求方式不支持

  • NotAcceptable 要获取的数据格式不支持

  • Throttled 超过限流次数

  • ValidationError 校验失败

也就是说,上面列出来的异常不需要我们自行处理了,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。

8. 自动生成接口文档

8.1 安装依赖

8.2 配置路由

8.3 配置setting文件

这样一个接口文档就自动生成了。

8.4 文档描述说明的定义位置

1) 单一方法的视图,可直接使用类视图的文档字符串,如

2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如

3)对于视图集ViewSet,仍在类视图的文档字符串中分开定义,但是应使用action名称区分,如

4) 视图集ViewSet中的 retrieve 名称,在接口文档网站中叫做read

5)参数的Description需要在模型类或序列化器类的字段中以help_text选项定义

或 注意,如果你多个应用使用同一个序列化器,可能会导致help_text的内容显示有些问题,小事情

Last updated