导图社区 RESTful架构--drf
我的python学习笔记, 用于学习复习以及方便查找
编辑于2020-07-01 10:46:08RESTful
RESTful API规范
web项目中操作资源
POST
添加数据
GET
获取数据
DELETE
删除数据
PUT
修改一条数据
PATCH
修改局部字段数据
序列化
api接口开发,最核心最常见的一个过程就是序列化
序列化就是把数据转换格式
序列化可以分两个阶段
序列化
把我们识别的数据转换成指定的格式提供给别人
我们在django中获取到的数据默认是模型对象
把模型对象的数据进行序列化,变成字符串或者json数据,提供给别人
反序列化
把别人提供的数据转换/还原成我们需要的格式
前端js提供过来的json数据,对于python而言就是字符串
需要进行反序列化换成模型类对象才能把数据保存到数据库中
RESTful API规范
域名
应该尽量将API部署在专用域名之下
https://api.example.com
版本
将API的版本号放入URL
http://www.example.com/app/1.0/foo
将版本号放在HTTP头信息中
不如放入URL方便和直观
版本号可以在HTTP请求头信息的Accept字段中进行区分
不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URL
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
路径
资源作为网址,只能有名词,不能有动词,而且所用的名词往往与数据库的表名对应
不要包含请求动词
get
post
delete
put
patch
API中的名词应该使用复数。无论子资源或者所有资源
获取单个产品:http://127.0.0.1:8080/AppName/rest/products/1
获取所有产品: http://127.0.0.1:8080/AppName/rest/products
HTTP动词
对于资源的具体操作类型,由HTTP动词表示
常用的动词
括号里是对应的SQL命令
- GET(SELECT):从服务器取出资源(一项或多项)。
- POST(CREATE):在服务器新建一个资源。
- PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
- DELETE(DELETE):从服务器删除资源。
不常用的HTTP动词
PATCH(UPDATE):在服务器更新(更新)资源(客户端提供改变的属性)。
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的。
过滤信息
完整的URL地址格式
协议://域名(IP):端口号/路径?查询字符串#锚点
如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
query_string 查询字符串
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件
参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复
GET /zoos/ID/animals
GET /animals?zoo_id=ID
含义是相同的
状态码
1xx 表示当前本次请求还是持续,没结束
2xx 表示当前本次请求成功
- 200 OK - [GET]:服务器成功返回用户请求的数据
- 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
- 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
- 204 NO CONTENT - [DELETE]:用户删除数据成功。
3xx 表示当前本次请求成功,但是服务器进行代理操作/重定向
4xx 表示当前本次请求失败,主要是客户端发生了错误
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作
- 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
- 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
- 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
- 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
- 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
- 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
5xx 表示当前本次请求失败,主要是服务器发生了错误
- 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
- 507 数据存储出错,往往数据库操作错误出错,服务器就返回这个
错误处理
状态码是4xx,服务器就应该向用户返回出错信息
一般来说,返回的信息中将error作为键名,出错信息作为键值即可
{ error: "Invalid API key" }
返回结果
针对不同操作,服务器向用户返回的结果应该符合以下规范。
- GET /collection:返回资源对象的列表(数组)
- GET /collection/ID:返回单个资源对象(json)
- POST /collection:返回新生成的资源对象(json)
- PUT /collection/ID:返回完整的资源对象(json)
- DELETE /collection/ID:返回一个空文档(空字符串)
超媒体
RESTful API最好做到Hypermedia(即返回结果中提供链接,连向其他API方法),使得用户不查文档,也知道下一步应该做什么
其他
服务器返回的数据格式,应该尽量使用JSON,避免使用XML
Django Rest_Framework
什么是Django Rest_Framework
一个建立在Django基础之上的Web 应用开发框架,可以快速的开发REST API接口应用
提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程
还提供丰富的类视图、扩展类、视图集来简化视图的编写工作
REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持
REST framework提供了一个API 的Web可视化界面来方便查看测试接口
中文文档
https://q1mi.github.io/Django-REST-framework-documentation/#django-rest-framework
特点
- 提供了定义序列化器Serializer的方法,可以快速根据 Django ORM 或者其它库自动序列化/反序列化;
- 提供了丰富的类视图、Mixin扩展类,简化视图的编写;
- 丰富的定制层级:函数视图、类视图、视图集合到自动生成 API,满足各种需要;
- 多种身份认证和权限认证方式的支持;[jwt]
- 内置了限流系统;
- 直观的 API web 界面;【方便我们调试开发api接口】
- 可扩展性,插件丰富
环境安装与配置
DRF需要依赖
- Python (2.7, 3.2以上)
- Django (1.10, 1.11, 2.0以上)
DRF是以Django扩展应用的方式提供的,所以我们可以直接利用已有的Django环境而无需从新创建
安装DRF
pip install djangorestframework -i https://pypi.douban.com/simple
添加rest_framework应用
在 settings.py 的 INSTALLED_APPS 中添加 'rest_framework'
INSTALLED_APPS = [ ... 'rest_framework', ]
体验drf完全简写代码
1. 创建模型操作类
class Student(models.Model): # 模型字段 name = models.CharField(max_length=100,verbose_name="姓名") sex = models.BooleanField(default=1,verbose_name="性别") age = models.IntegerField(verbose_name="年龄") class_null = models.CharField(max_length=5,verbose_name="班级编号") description = models.TextField(max_length=1000,verbose_name="个性签名") class Meta: db_table="tb_student" verbose_name = "学生" verbose_name_plural = verbose_name
2. 创建数据库
create database students charset=utf8;
3. 执行数据迁移
初始化数据库连接
安装pymysql
pip install pymysql
设置使用pymysql作为数据库驱动
主引用中`__init__.py`
import pymysql pymysql.install_as_MySQLdb()
settings.py配置文件中设置mysql的账号密码
DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # }, 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': "students", "HOST": "127.0.0.1", "PORT": 3306, "USER": "root", "PASSWORD":"123", }, }
终端下,执行数据迁移
python manage.py makemigrations
python manage.py migrate
4. 创建序列化器
在django项目中创建学生子应用
python manage.py startapp students
在syudents应用目录中新建serializers.py用于保存该应用的序列化器
# 创建一个StudentModelSerializer用于序列化与反序列化 # 创建序列化器类,回头会在视图中被调用 class StudentModelSerializer(serializers.ModelSerializer): class Meta: model = Student fields = "__all__"
model 指明该序列化器处理的数据字段从模型类BookInfo参考生成
fields 指明该序列化器包含模型类中的哪些字段,'__all__'指明包含所有字段
5. 编写视图
在students应用的views.py中创建视图StudentViewSet,这是一个视图集合
from rest_framework.viewsets import ModelViewSet from .models import Student from .serializers import StudentModelSerializer # Create your views here. class StudentViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer
- queryset 指明该视图集在查询数据时使用的查询集
- serializer_class 指明该视图在进行序列化或反序列化时使用的序列化器
6. 定义路由
在students应用的urls.py中定义路由信息
from . import views from rest_framework.routers import DefaultRouter # 路由列表 urlpatterns = [] router = DefaultRouter() # 可以处理视图的路由器 router.register('students', views.StudentViewSet) # 向路由器中注册视图集 urlpatterns += router.urls # 将路由器中的所以路由信息追到到django的路由列表中
加载到总路由文件中
from django.contrib import admin from django.urls import path,include urlpatterns = [ path('admin/', admin.site.urls), path("stu/",include("students.urls")), ]
7. 运行测试
python manage.py runserver
drf--序列化器Serializer
作用
1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能
注意
serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义
serializer是独立于数据库之外的存在。
序列化器字段类型
BooleanField
CharField
IntegerField
FloatField
DecimalField
DateTimeField
DateField
ChoiceField
MultipleChoiceField
ImageField
字段的选项参数
max_length
最大长度
min_lenght
最小长度
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页面时,显示的字段帮助提示信息
定义序列化器的步骤
定义一个序列化器类
在子应用中新建serializers.py用于保存该应用的序列化器
序列化器类须继承自rest_framework.serializers.Serializer
类名称格式: 模型+父类名
1. 需要进行数据转换的字段
2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息
3. 验证代码
4. 编写添加和更新模型的代码
创建Serializer对象
定义序列化器
前期准备
注册drf
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', # 把drf框架注册到django项目中 'students', # 注册子应用 'sers', ]
模型类
class Student(models.Model): # 模型字段 name = models.CharField(max_length=100,verbose_name="姓名") sex = models.BooleanField(default=1,verbose_name="性别") age = models.IntegerField(verbose_name="年龄") class_number = models.CharField(max_length=5,verbose_name="班级编号") description = models.TextField(max_length=1000,verbose_name="个性签名") class Meta: db_table="tb_student" verbose_name = "学生" verbose_name_plural = verbose_name
序列化器类
from rest_framework import serializers # 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer # 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化 class StudentSerializer(serializers.Serializer): """学生信息序列化器""" # 1. 需要进行数据转换的字段 id = serializers.IntegerField() name = serializers.CharField() age = serializers.IntegerField() sex = serializers.BooleanField() description = serializers.CharField()
定义好Serializer类后,就可以创建Serializer对象了
Serializer(instance=None, data=empty, **kwarg)
用于序列化时,将模型类对象传入 instance 参数
用于反序列化时,将要被反序列化的数据传入 data 参数
除了instance和data参数外,在构造Serializer对象时,还可通过 context 参数额外添加数据
通过context参数附加的数据,可以通过Serializer对象的self.context属性获取
注意
1. 使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以。
2. 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。
3. 序列化器的字段声明类似于我们前面使用过的表单系统。
4. 开发restful api时,序列化器会帮我们把模型数据转换成字典。
5. drf提供的视图会帮我们把字典转换成json,或者把客户端发送过来的数据转换字典。
序列化器的使用 Serializer
序列化器的使用分两个阶段
1. 在客户端请求时,使用序列化器可以完成对数据的反序列化。
2. 在服务器响应时,使用序列化器可以完成对数据的序列化。
序列化
1) 先查询出一个学生对象
from students.models import Student student = Student.objects.get(id=3)
2) 构造序列化器对象
from .serializers import StudentSerializer serializer = StudentSerializer(instance=student)
3)获取序列化数据
serializer.data
通过data属性可以获取序列化后的数据
# {'id': 4, 'name': '小张', 'age': 18, 'sex': True, 'description': '猴赛雷'}
示例
获取到一条数据
from django.views import View from students.models import Student from .serializers import StudentSerializer from django.http.response import JsonResponse class StudentView(View): """使用序列化器序列化转换单个模型数据""" def get(self,request,pk): # 获取数据 student = Student.objects.get(pk=pk) # 数据转换[序列化过程] serializer = StudentSerializer(instance=student) print(serializer.data) # 响应数据 return JsonResponse(serializer.data)
获取多条数据
"""使用序列化器序列化转换多个模型数据""" def get(self,request): # 获取数据 student_list = Student.objects.all() # 转换数据[序列化过程] # 如果转换多个模型对象数据,则需要加上many=True serializer = StudentSerializer(instance=student_list,many=True) print( serializer.data ) # 序列化器转换后的数据 # 响应数据给客户端 # 返回的json数据,如果是列表,则需要声明safe=False return JsonResponse(serializer.data,safe=False)
如果要被序列化的是包含多条数据的查询集QuerySet, 可以通过添加 many=True 参数补充说明
反序列化
验证数据
数据验证
什么是数据验证
使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象
在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为
怎么数据验证
在获取反序列化的数据前,必须调用 is_valid() 方法
序列化器内部是再 is_valid() 方法内部进行验证,验证成功返回True,否则返回False
验证失败
通过序列化器对象的**errors**属性获取错误信息,返回字典,包含了字段和字段的错误
如果是非字段错误,可以通过修改REST framework配置中的 NON_FIELD_ERRORS_KEY 来控制错误字典中的键名
验证成功
可以通过序列化器对象的validated_data属性获取数据
注意: 在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为
模型类
from django.db import models # Create your models here. class BookInfo(models.Model): """图书信息""" title = models.CharField(max_length=20, verbose_name='标题') pub_date = models.DateField(verbose_name='发布日期') image = models.ImageField(verbose_name='图书封面') price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="价格") read = models.IntegerField(verbose_name='阅读量') comment = models.IntegerField(verbose_name='评论量') class Meta: # db_table = "表名" db_table = "tb_book_info" verbose_name = "图书" verbose_name_plural = verbose_name
序 列 化 器
验证写在serializer.py中
from rest_framework import serializers class BookInfoSerializer(serializers.Serializer): # 这里声明的字段用于进行反序列化器 # 字段名 = serializers.字段类型(验证选项) title = serializers.CharField(max_length=20, label="标题", help_text="标题") # required=True 当前字段必填 pub_date = serializers.DateField(required=True,label="发布日期", help_text="发布日期") image = serializers.ImageField(max_length=3*1024*1024, label="图书封面", help_text="图书封面") price = serializers.DecimalField(max_digits=8, decimal_places=2, required=True, label="价格", help_text="价格") read = serializers.IntegerField(min_value=0, default=0, label="阅读量", help_text="阅读量") comment = serializers.IntegerField(min_value=0, default=0, label="评论量", help_text="评论量")
视图类
from django.views import View from django.http.response import HttpResponse from .serializers import BookInfoSerializer class BookInfoView(View): def get(self,request): """模拟客户端发送过来的数据""" data = { "title":"西厢记", "pub_date":"1980-10-10", "price": 19.80, "read": 100, "comment": -1, } # 对上面的数据进行反序列化器处理 # 1. 初始化,填写data属性 serializer = BookInfoSerializer(data=data) # 2. 调用序列化器提供的is_valid方法进行验证 # raise_exception=True 表示终断程序,直接抛出错误 ret = serializer.is_valid(raise_exception=True) print(ret) # is_valid的方法值就是验证结果,只会是True/False if ret: # 3.1 验证通过后,可以通过validated_data得到数据 print("验证成功,ret=%s" % ret) print(serializer.validated_data) # 验证处理后的数据 """打印结果: OrderedDict([('title', '西厢记'), ('pub_date', datetime.date(1980, 10, 10)), ('price', Decimal('19.80')), ('read', 100), ('comment', 15)]) """ else: print("验证失败,ret=%s" % ret) # 3.1 验证没通过,可以通过 print( serializer.errors ) """打印结果: {'comment': [ErrorDetail(string='Ensure this value is greater than or equal to 0.', code='min_value')]} """ return HttpResponse("ok")
is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError
可以通过传递raise_exception=True参数开启
补充定义验证行为
validate_字段名
class BookInfoSerializer(serializers.Serializer): ....... # 单个字段的验证,方法名必须: validate_<字段名>(self,data) # data 就是当前字段中客户端提交的数据 # validate_price 会被is_valid调用 def validate_price(self, data): """""" if data < 0: raise serializers.ValidationError("对不起,价格不能低于0元") # 验证通过以后,必须要返回验证的结果数据,否则序列化器的validated_data无法得到当前字段的结果 return data
validate
需要同时对多个字段进行比较验证时,可以定义validate方法来验证
class BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" ... # 多个字段的验证,必须方法名叫 "validate" # data 表示客户端发送过来的所有数据,字典格式 def validate(self, data): # 判断图书的阅读量不能低于评论量 read = data.get("read") comment = data.get("comment") if read < comment: raise serializers.ValidationError("对不起,阅读量不能低于评论量") return data
validators
字段中添加validators选项参数,也可以补充验证行为
from rest_framework import serializers # 可以把验证函数进行多次使用,提供不用的字段或者不同的序列化器里面使用 def about_django(data): if "django" in data: raise serializers.ValidationError("对不起,图书标题不能出现关键字django") # 返回验证以后的数据 return data class BookInfoSerializer(serializers.Serializer): # 这里声明的字段用于进行反序列化器 # 字段名 = serializers.字段类型(验证选项) title = serializers.CharField(max_length=20,validators=[about_django], label="标题", help_text="标题") # required=True 当前字段必填 pub_date = serializers.DateField(required=True, label="发布日期", help_text="发布日期") # max_length 文件的大小 # allow_null=True 允许传递的image数据为None image = serializers.ImageField(required=False, allow_null=True, max_length=3*1024*1024, label="图书封面", help_text="图书封面") price = serializers.DecimalField(max_digits=8, decimal_places=2, required=True, label="价格", help_text="价格") # min_value 数值大小 # default 设置默认值 read = serializers.IntegerField(min_value=0, default=0, label="阅读量", help_text="阅读量") comment = serializers.IntegerField(min_value=0, default=0, label="评论量", help_text="评论量")
is_valid() 验证的内容
1. 先执行了字段内置的字段选项参数
2. 在执行了字段中的validators自定义选项
3. 最后执行了validate自定义验证方法[包含了validate_<字段>, validate]
操作数据
什么是数据操作
完成数据的更新或者添加
前面的验证数据成功后,我们可以使用序列化器来完成数据反序列化的过程
这个过程可以把数据转成模型类对象
可以通过实现create()和update()两个方法来实现
get和delete不需要进入序列化器
操作数据的两个步骤
1. 在serializer.py中编写create() / update() 方法
create
def create(self, validated_data): # 这里会在调用时,由序列化器补充验证成功以后的数据进来 """完成添加操作""" print(validated_data) # 字典 # 导入模型 from .models import BookInfo # 添加数据 book = BookInfo.objects.create( title=validated_data.get("title"), price=validated_data.get("price"), pub_date=validated_data.get("pub_date"), read=validated_data.get("read"), comment=validated_data.get("comment"), ) return book
这里会在调用时,由序列化器补充验证成功以后的数据 data 传进来
这里使用的也是django的ORM
update
def update(self, instance, validated_data): """更新操作""" instance.title = validated_data.get('title') instance.pub_date = validated_data.get('pub_date') instance.comment = validated_data.get('comment') instance.price = validated_data.get('price') instance.read = validated_data.get('read') instance.save() return instance
instance就是要修改的模型,系统会自动从对象初始化时的instance提取过来
validated_data 就是经过验证以后的客户端提交的数据
这里使用的也是django的ORM
2. 在视图中序列化器对象.save()
serializer = BookInfoSerializer(instance=book,data=data) serializer.save()
这里的save不是数据库ORM模型对象的save,是BaseSerializer定义的
save方法中根据实例化serializer时是否传入instance参数来判断执行update还是create的
当传入instance时,则instance.save调用的就是update方法
没有传入instance,则instance.save调用的就是create方法
serializer.save使用前提是必须在序列化器中声明create或者update方法,否则报错
serailzier对象调用的save方法是什么? 怎么做到自动调用update和create?
这里的save不是数据库ORM模型对象的save,是BaseSerializer定义的
save方法中根据实例化serializer时是否传入instance参数来判断执行update还是create的
当传入instance时,则instance.save调用的就是update方法
没有传入instance,则instance.save调用的就是create方法
serializer.save使用前提是必须在序列化器中声明create或者update方法,否则报错
进行save()时,额外传递数据
这些数据可以在create()和update()中的validated_data参数获取到
# request.user 是django中记录当前登录用户的模型对象 serializer.save(owner=request.user)
默认序列化器必须传递所有required的字段,否则会抛出验证异常
我们可以使用partial参数来允许部分字段更新
serializer = BookInfoSerializer(book, data=data, partial=True)
示例
serializer.py
from rest_framework import serializers # 可以把验证函数进行多次使用,提供不用的字段或者不同的序列化器里面使用 def about_django(data): if "django" in data: raise serializers.ValidationError("对不起,图书标题不能出现关键字django") # 返回验证以后的数据 return data class BookInfoSerializer(serializers.Serializer): # 这里声明的字段用于进行反序列化器 # 字段名 = serializers.字段类型(验证选项) title = serializers.CharField(max_length=20,validators=[about_django], label="标题", help_text="标题") # required=True 当前字段必填 pub_date = serializers.DateField(required=True, label="发布日期", help_text="发布日期") # max_length 文件的大小 # allow_null=True 允许传递的image数据为None image = serializers.ImageField(required=False, allow_null=True, max_length=3*1024*1024, label="图书封面", help_text="图书封面") price = serializers.DecimalField(max_digits=8, decimal_places=2, required=True, label="价格", help_text="价格") # min_value 数值大小 # default 设置默认值 read = serializers.IntegerField(min_value=0, default=0, label="阅读量", help_text="阅读量") comment = serializers.IntegerField(min_value=0, default=0, label="评论量", help_text="评论量") # 关于继承数据库选项 # 自定义验证的代码 # 单个字段的验证,方法名必须: validate_<字段名>(self,data) # data 就是当前字段中客户端提交的数据 # validate_price 会被is_valid调用 def validate_price(self, data): """""" if data < 0: raise serializers.ValidationError("对不起,价格不能低于0元") # 验证通过以后,必须要返回验证的结果数据,否则序列化器的validated_data无法得到当前字段的结果 return data # 多个字段的验证,必须方法名叫 "validate" # data 表示客户端发送过来的所有数据,字典格式 def validate(self, data): # 判断图书的阅读量不能低于评论量 read = data.get("read") comment = data.get("comment") if read < comment: raise serializers.ValidationError("对不起,阅读量不能低于评论量") return data # 数据库操作 def create(self, validated_data): # 这里会在调用时,由序列化器补充验证成功以后的数据进来 """完成添加操作""" print(validated_data) # 字典 # 导入模型 from .models import BookInfo # 添加数据 book = BookInfo.objects.create( title=validated_data.get("title"), price=validated_data.get("price"), pub_date=validated_data.get("pub_date"), read=validated_data.get("read"), comment=validated_data.get("comment"), ) return book # instance就是要修改的模型,系统会自动从对象初始化时的instance提取过来 # validated_data 就是经过验证以后的客户端提交的数据 def update(self, instance, validated_data): """更新操作""" instance.title = validated_data.get('title') instance.pub_date = validated_data.get('pub_date') instance.comment = validated_data.get('comment') instance.price = validated_data.get('price') instance.read = validated_data.get('read') instance.save() return instance
views.py
# Create your views here. from django.views import View from django.http.response import HttpResponse from .serializers import BookInfoSerializer class BookInfoView(View): # ... def get(self,request): """保存数据[更新]""" # 客户端提交数据过来 id = 2 data = { # 模拟客户端发送过来的数据 "title": "东游记", "pub_date": "1998-10-01", "price": 19.98, "read": 330, "comment": 100, } from .models import BookInfo book = BookInfo.objects.get(pk=id) # 使用序列化器验证数据[如果是更新操作,需要传入2个参数,分别是instance和data] serializer = BookInfoSerializer(instance=book,data=data) serializer.is_valid() book = serializer.save() # 此时,我们必须在序列化器中预先声明update方法 """ serailzier对象调用的save方法是什么?怎么做到自动调用update和create? 1. 这里的save不是数据库ORM模型对象的save,是BaseSerializer定义的。 2. save方法中根据实例化serializer时是否传入instance参数来判断执行update还是create的 当传入instance时,则instance.save调用的就是update方法 没有传入instance,则instance.save调用的就是create方法 3. serializer.save使用前提是必须在序列化器中声明create或者update方法,否则报错!!! """ print(book) """打印结果: BookInfo object (2) """ return HttpResponse("ok")
模型类序列化器 ModeSerializer
DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类
ModeSerializer与Serializer之间的区别
ModeSerializer就是Serializer的进一步封装
- 基于模型类自动生成一系列字段
- 基于模型类自动为Serializer生成validators,比如unique_together
- 包含默认的create()和update()的实现
示例
class BookInfoSerializer(serializers.ModelSerializer): """图书数据序列化器""" class Meta: model = BookInfo fields = '__all__'
- model 指明参照哪个模型类
- fields 指明为模型类的哪些字段生成
指定字段
使用 fields 来明确字段,`__all__`表名包含所有字段,也可以写明具体哪些字段
fields = '__all__'
fields = ('id', 'btitle', 'bpub_date')
使用exclude可以明确排除掉哪些字段
exclude = ('image',)
指明只读字段
可以通过**read_only_fields**指明只读字段,即仅用于序列化输出的字段
class BookInfoSerializer(serializers.ModelSerializer): """图书数据序列化器""" class Meta: model = BookInfo fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment') read_only_fields = ('id', 'bread', 'bcomment')
添加额外参数
我们可以使用extra_kwargs参数为ModelSerializer添加或修改原有的选项参数
class BookInfoSerializer(serializers.ModelSerializer): """图书数据序列化器""" class Meta: model = BookInfo fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment') extra_kwargs = { 'bread': {'min_value': 0, 'required': True}, 'bcomment': {'min_value': 0, 'required': True}, }
声明的序列化器需要继承基类Serializer,还是继承ModelSerializer
继承序列化器类Serializer
字段声明
验证
添加/保存数据功能
继承模型序列化器类ModelSerializer
字段声明[可选,看需要]
Meta声明
验证
添加/保存数据功能[可选]
drf--视图(请求/响应)
Django REST framwork 提供的视图的主要作用
- 控制序列化器的执行(检验、保存、转换数据)
- 控制数据库查询的执行
- 调用请求类和响应类[这两个类也是由drf帮我们再次扩展了一些功能类。]
请求Request
Request对象的数据是自动根据前端发送数据的格式进行解析之后的结果
REST framework 传入视图的request对象不再是Django默认的HttpRequest对象
而是REST framework提供的扩展了HttpRequest类的 Request 类的对象
接收到请求后会自动根据Content-Type指明的请求数据类型(如JSON、表单等)
将请求数据进行parse解析
解析为类字典[QueryDict]对象保存到**Request**对象中
常用属性
request.data 返回解析之后的请求体数据,转换成字典
request.query_params与Django框架的request.GET相同
响应Response
rest_framework.response.Response
使用该类构造响应对象时
响应的具体数据内容会被转换(render渲染器)成符合前端需求的类型
根据请求头中的`Accept`(接收数据类型声明)来自动转换响应数据到对应格式
如请求中未声明Accept,则会采用默认方式处理响应数据
可以通过配置来修改默认响应格式
在 rest_framework.settings 查找所有的drf默认配置项
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer', # 浏览器API渲染器 ) }
构造方式: Response(data, status=None, template_name=None, headers=None, content_type=None)
data: 为响应准备的序列化处理后的数据;
status: 状态码,默认200;
template_name: 模板名称,如果使用HTMLRenderer时需指明;
headers: 用于存放响应头信息的字典;
content_type: 响应数据的Content-Type
通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数
常用属性
.data
传给response对象的序列化后,但尚未render处理的数据
.status_code
状态码的数字
.content
经过render处理后的响应数据
状态码
HTTP_100_CONTINUE HTTP_101_SWITCHING_PROTOCOLS
HTTP_200_OK HTTP_201_CREATED HTTP_202_ACCEPTED HTTP_203_NON_AUTHORITATIVE_INFORMATION HTTP_204_NO_CONTENT HTTP_205_RESET_CONTENT HTTP_206_PARTIAL_CONTENT HTTP_207_MULTI_STATUS
HTTP_300_MULTIPLE_CHOICES HTTP_301_MOVED_PERMANENTLY HTTP_302_FOUND HTTP_303_SEE_OTHER HTTP_304_NOT_MODIFIED HTTP_305_USE_PROXY HTTP_306_RESERVED HTTP_307_TEMPORARY_REDIRECT
HTTP_400_BAD_REQUEST HTTP_401_UNAUTHORIZED HTTP_402_PAYMENT_REQUIRED HTTP_403_FORBIDDEN HTTP_404_NOT_FOUND HTTP_405_METHOD_NOT_ALLOWED HTTP_406_NOT_ACCEPTABLE HTTP_407_PROXY_AUTHENTICATION_REQUIRED HTTP_408_REQUEST_TIMEOUT HTTP_409_CONFLICT HTTP_410_GONE HTTP_411_LENGTH_REQUIRED HTTP_412_PRECONDITION_FAILED HTTP_413_REQUEST_ENTITY_TOO_LARGE HTTP_414_REQUEST_URI_TOO_LONG HTTP_415_UNSUPPORTED_MEDIA_TYPE HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE HTTP_417_EXPECTATION_FAILED HTTP_422_UNPROCESSABLE_ENTITY HTTP_423_LOCKED HTTP_424_FAILED_DEPENDENCY HTTP_428_PRECONDITION_REQUIRED HTTP_429_TOO_MANY_REQUESTS HTTP_431_REQUEST_HEADER_FIELDS_TOO_LARGE HTTP_451_UNAVAILABLE_FOR_LEGAL_REASONS
HTTP_500_INTERNAL_SERVER_ERROR HTTP_501_NOT_IMPLEMENTED HTTP_502_BAD_GATEWAY HTTP_503_SERVICE_UNAVAILABLE HTTP_504_GATEWAY_TIMEOUT HTTP_505_HTTP_VERSION_NOT_SUPPORTED HTTP_507_INSUFFICIENT_STORAGE HTTP_511_NETWORK_AUTHENTICATION_REQUIRED
drf--视图(视图基类)
APIView
rest_framework.views.APIView
继承自Django的`View`父类
`APIView`与`View`的不同之处在于
传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象
视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
任何APIException异常都会被捕获到,并且处理成合适的响应信息;
在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
属性
authentication_classes 列表或元祖,身份认证类
permissoin_classes 列表或元祖,权限检查类
throttle_classes 列表或元祖,流量控制类
示例
GET /students/ 获取多个学生信息 POST /students/ 添加一个学生信息
from rest_framework.views import APIView from students.models import Student from .serializers import StudentModelSerializer from rest_framework.response import Response from rest_framework import status class StudentAPIView(APIView): def get(self,request): # 1. 获取学生信息的数据模型 student_list = Student.objects.all() # 2. 调用序列化器 serializer = StudentModelSerializer(instance=student_list, many=True) # 3. 返回数据 return Response(serializer.data) def post(self,request): # 1. 调用序列化器对用户提交的数据进行验证 serializer = StudentModelSerializer(data=request.data) serializer.is_valid(raise_exception=True) # 2. 调用序列化器进行数据库操作 serializer.save() # save()方法返回的是添加成功以后的模型对象 # 3. 返回新增数据 return Response(serializer.data, status=status.HTTP_201_CREATED)
GET /students/<pk>/ 获取一个学生信息 PUT /students/<pk>/ 修改一个学生信息 DELETE /students/<pk>/ 删除一个学生信息
from rest_framework.views import APIView from students.models import Student from .serializers import StudentModelSerializer from rest_framework.response import Response from rest_framework import status class Student2APIView(APIView): def get(self,request,pk): # 1. 根据pk获取模型对象 student = Student.objects.get(pk=pk) # 2. 序列化器转换数据 serializer = StudentModelSerializer(instance=student) # 3. 响应数据 return Response(serializer.data) def put(self,request,pk): # 1. 通过pk查询学生信息 student = Student.objects.get(pk=pk) # 3. 调用序列化器对客户端发送过来的数据进行验证 serializer = StudentModelSerializer(instance=student, data=request.data) serializer.is_valid(raise_exception=True) # 4. 保存数据 serializer.save() # 5. 返回结果 return Response(serializer.data, status=status.HTTP_201_CREATED) def delete(self, request, pk): # 1. 通过pk查询学生信息 Student.objects.get(pk=pk).delete() return Response({"message":"ok"}, status=status.HTTP_204_NO_CONTENT)
GenericAPIView 通用视图类
rest_framework.generics.GenericAPIView
继承自`APIVIew`
主要增加了操作序列化器和数据库查询的方法
作用是为下面Mixin扩展类的执行提供方法支持
通常在使用时,可搭配一个或多个Mixin扩展类
属性
列表视图与详情视图通用
queryset 列表视图的查询集
serializer_class 视图使用的序列化器
列表视图使用
pagination_class 分页控制类
filter_backends 过滤控制后端
详情页视图使用
lookup_field 查询单一数据库对象时使用的条件字段,默认为'pk'
lookup_url_kwarg 查询单一数据时URL中的参数关键字名称,默认与look_field相同
方法
列表视图与详情视图通用
get_queryset(self) 获取所有数据
返回视图使用的查询集,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写
get_serializer_class(self) 调整序列化器类
返回序列化器类,默认返回serializer_class,可以重写
get_serializer(self, args, *kwargs) 获取序列化器对象
返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法
详情视图使用
get_object(self) 获取一条数据
返回详情视图所需的模型类数据对象,默认使用lookup_field参数来过滤queryset
示例
视图
from rest_framework.generics import GenericAPIView from students.models import Student from .serializers import StudentModelSerializer, StudentModel2Serializer from rest_framework.response import Response class StudentsGenericAPIView(GenericAPIView): # 本次视图类中要操作的数据[必填] queryset = Student.objects.all() # 本次视图类中要调用的默认序列化器[选填] serializer_class = StudentModelSerializer def get(self, request): """获取所有学生信息""" serializer = self.get_serializer(instance=self.get_queryset(), many=True) return Response(serializer.data) def post(self,request): data = request.data serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) instance = serializer.save() serializer = self.get_serializer(instance=instance) return Response(serializer.data) class StudentGenericAPIView(GenericAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer def get_serializer_class(self): """重写获取序列化器类的方法""" if self.request.method == "GET": return StudentModel2Serializer else: return StudentModelSerializer # 在使用GenericAPIView视图获取或操作单个数据时,视图方法中的代表主键的参数最好是pk def get(self,request,pk): """获取一条数据""" serializer = self.get_serializer(instance=self.get_object()) return Response(serializer.data) def put(self,request,pk): data = request.data serializer = self.get_serializer(instance=self.get_object(),data=data) serializer.is_valid(raise_exception=True) serializer.save() serializer = self.get_serializer(instance=self.get_object()) return Response(serializer.data)
Serializer.py
from rest_framework import serializers from students.models import Student class StudentModelSerializer(serializers.ModelSerializer): class Meta: model= Student fields = "__all__" class StudentModel2Serializer(serializers.ModelSerializer): class Meta: model= Student fields = ("name","class_null")
视图扩展类(Mixins)
CreateModelMixin[添加一条数据]
源码
class CreateModelMixin(object): """ 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 {}
ListModelMixin[获取所有数据]
源码
class ListModelMixin(object): """ List a queryset. """ def list(self, request, *args, **kwargs): # 过滤 queryset = self.filter_queryset(self.get_queryset()) # 分页 page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) # 序列化 serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
示例
from rest_framework.mixins import ListModelMixin class BookListView(ListModelMixin, GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request): return self.list(request)
RetrieveModelMixin[获取一条数据]
源码
class RetrieveModelMixin(object): """ Retrieve a model instance. """ def retrieve(self, request, *args, **kwargs): # 获取对象,会检查对象的权限 instance = self.get_object() # 序列化 serializer = self.get_serializer(instance) return Response(serializer.data)
示例
class BookDetailView(RetrieveModelMixin, GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer def get(self, request, pk): return self.retrieve(request)
DestroyModelMixin[删除一条数据]
源码
class UpdateModelMixin(object): """ Update a model instance. """ def update(self, request, *args, **kwargs): partial = kwargs.pop('partial', False) instance = self.get_object() serializer = self.get_serializer(instance, data=request.data, partial=partial) serializer.is_valid(raise_exception=True) self.perform_update(serializer) if getattr(instance, '_prefetched_objects_cache', None): # If 'prefetch_related' has been applied to a queryset, we need to # forcibly invalidate the prefetch cache on the instance. instance._prefetched_objects_cache = {} return Response(serializer.data) def perform_update(self, serializer): serializer.save() def partial_update(self, request, *args, **kwargs): kwargs['partial'] = True return self.update(request, *args, **kwargs)
UpdateModelMixin[更新一条数据]
源码
class DestroyModelMixin(object): """ Destroy a model instance. """ def destroy(self, request, *args, **kwargs): instance = self.get_object() self.perform_destroy(instance) return Response(status=status.HTTP_204_NO_CONTENT) def perform_destroy(self, instance): instance.delete()
示例
"""GenericAPIView结合视图扩展类实现api接口""" from rest_framework.mixins import ListModelMixin,CreateModelMixin class Students2GenericAPIView(GenericAPIView,ListModelMixin,CreateModelMixin): # 本次视图类中要操作的数据[必填] queryset = Student.objects.all() # 本次视图类中要调用的默认序列化器[玄天] serializer_class = StudentModelSerializer def get(self, request): """获取多个学生信息""" return self.list(request) def post(self,request): """添加学生信息""" return self.create(request) from rest_framework.mixins import RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin class Student2GenericAPIView(GenericAPIView,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer # 在使用GenericAPIView视图获取或操作单个数据时,视图方法中的代表主键的参数最好是pk def get(self,request,pk): """获取一条数据""" return self.retrieve(request,pk) def put(self,request,pk): """更新一条数据""" return self.update(request,pk) def delete(self,request,pk): """删除一条数据""" return self.destroy(request,pk)
GenericAPIView子类
CreateAPIView
提供 post 方法
继承自: GenericAPIView、CreateModelMixin
ListAPIView
提供 get 方法
继承自:GenericAPIView、ListModelMixin
RetireveAPIView
提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
DestoryAPIView
提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
UpdateAPIView
提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
RetrieveUpdateAPIView
提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
视图集ViewSet
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中
- list() 提供一组数据
- retrieve() 提供单个数据
- create() 创建数据
- update() 保存数据
- destory() 删除数据
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等
视图集只在使用as_view()方法的时候,才会将**action**动作与具体请求方式对应上
ViewSet
继承自`APIView`与`ViewSetMixin`,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
**ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{'get':'list'})的映射处理工作。**
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
示例
视图
class BookInfoViewSet(viewsets.ViewSet): def list(self, request): books = BookInfo.objects.all() serializer = BookInfoSerializer(books, many=True) return Response(serializer.data) def retrieve(self, request, pk=None): try: books = BookInfo.objects.get(id=pk) except BookInfo.DoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) serializer = BookInfoSerializer(books) return Response(serializer.data)
路由
urlpatterns = [ url(r'^books/$', BookInfoViewSet.as_view({'get':'list'}), url(r'^books/(?P<pk>\d+)/$', BookInfoViewSet.as_view({'get': 'retrieve'}) ]
视图集GenericViewSet
使用ViewSet通常并不方便
因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写
但是Mixin扩展类依赖与`GenericAPIView`,所以还需要继承`GenericAPIView`
GenericViewSet 就帮助我们完成了这样的继承工作,继承自`GenericAPIView`与`ViewSetMixin`,在实现了调用as_view()时传入字典(如`{'get':'list'}`)的映射处理工作的同时,还提供了`GenericAPIView`提供的基础方法,可以直接搭配Mixin扩展类使用
示例
视图
from rest_framework.viewsets import GenericViewSet from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin class Student4ViewSet(GenericViewSet,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): queryset = Student.objects.all() serializer_class = StudentModelSerializer
路由
urlpatterns = [ path("students7/", views.Student4ViewSet.as_view({"get": "list", "post": "create"})), re_path("students7/(?P<pk>\d+)/", views.Student4ViewSet.as_view({"get": "retrieve","put":"update","delete":"destroy"})), ]
视图集ModelViewSet
继承自`GenericViewSet`,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin
视图集ReadOnlyModelViewSet
继承自`GenericViewSet`,同时包括了ListModelMixin、RetrieveModelMixin
视图集中定义附加action动作
示例
视图
from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet class StudentModelViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentModelSerializer def login(self,request): """学生登录功能""" return Response({"message":"登录成功"})
路由
urlpatterns = [ path("students8/", views.StudentModelViewSet.as_view({"get": "list", "post": "create"})), re_path("students8/(?P<pk>\d+)/", views.StudentModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})), path("stu/login/",views.StudentModelViewSet.as_view({"get":"login"})) ]
drf--路由
对于视图集ViewSet
可以自己手动在路由url.py中设置http请求方式与动作action之间的对应关系
在drf中还可以使用Routers路由类来自动帮我们快速生成视图集的路由列表
REST framework提供了两个router路由类
SimpleRouter
DefaultRouter
创建router对象,并注册视图集
from rest_framework import routers router = routers.DefaultRouter() router.register('router/student', StudentModelViewSet, basename='student')
register(prefix, viewset, base_name)
prefix 该视图集的路由前缀
viewset 视图集
basename 路由别名的前缀
添加路由数据
urlpatterns = [ ... ] urlpatterns += router.urls
urlpatterns = [ ... url(r'^', include(router.urls)) ]
drf--组件模块
认证Authentication
配合权限组件使用
设置启用Authentication组件
/.virtualenvs/drfdemo/lib/python3.6/site-packages/rest_framework/settings.py
修改
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', # session认证 'rest_framework.authentication.BasicAuthentication', # 基本认证 ) }
视图集中调用Authentication组件
from rest_framework.authentication import SessionAuthentication, BasicAuthentication from rest_framework.views import APIView class ExampleView(APIView): # 类属性 authentication_classes = [SessionAuthentication, BasicAuthentication] def get(self,request): pass
权限Permissions
权限控制
可以限制用户对于视图的访问和对于具体数据对象的访问
权限使用
在settings中设置
REST_FRAMEWORK = { .... 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ) }
如果未指明
采用如下默认配置
'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.AllowAny', )
视图中
from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.generics import RetrieveAPIView class StudentAPIView(RetrieveAPIView): queryset = Student.objects.all() serializer_class = StudentSerializer authentication_classes = [SessionAuthentication] permission_classes = [IsAuthenticated]
权限级别
AllowAny
允许所有用户
IsAuthenticated
仅通过登录认证的用户
IsAdminUser
仅管理员用户
IsAuthenticatedOrReadOnly
已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据
自定义权限
自定义权限
需继承rest_framework.permissions.BasePermission父类
并实现以下两个任何一个方法或全部
.has_permission(self, request, view)
是否可以访问视图, view表示当前视图对象
.has_object_permission(self, request, view, obj)
是否可以访问数据对象, view表示当前视图, obj为模型数据对象
示例
在子应用下创建permission.py
声明自定义权限类
from rest_framework.permissions import BasePermission class IsXiaoMingPermission(BasePermission): def has_permission(self, request, view): if( request.user.username == "xiaoming" ): return True
视图
from .permissions import IsXiaoMingPermission class StudentViewSet(ModelViewSet): queryset = Student.objects.all() serializer_class = StudentSerializer permission_classes = [IsXiaoMingPermission]
限流Throttling
限流使用
在settings中设置
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.AnonRateThrottle', 'rest_framework.throttling.UserRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', 'user': '1000/day' } }
DEFAULT_THROTTLE_RATES
可以使用 second , minute , hour 或 day 来指明周期
视图
from rest_framework.authentication import SessionAuthentication from rest_framework.permissions import IsAuthenticated from rest_framework.generics import RetrieveAPIView from rest_framework.throttling import UserRateThrottle class StudentAPIView(RetrieveAPIView): queryset = Student.objects.all() serializer_class = StudentSerializer authentication_classes = [SessionAuthentication] permission_classes = [IsAuthenticated] throttle_classes = (UserRateThrottle,)
可选限流类
AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户
使用 DEFAULT_THROTTLE_RATES['anon'] 来设置频次
UserRateThrottle
限制认证用户,使用User id 来区分
使用 DEFAULT_THROTTLE_RATES['user'] 来设置频次
ScopedRateThrottle
限制用户对于每个视图的访问频次,使用ip或user id
REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( 'rest_framework.throttling.ScopedRateThrottle', ), 'DEFAULT_THROTTLE_RATES': { 'contacts': '1000/day', 'uploads': '20/day' } }
过滤Filtering
安装拓展
pip install django-filter
注册应用
INSTALLED_APPS = [ ... 'django_filters', # 需要注册应用, ]
注册过滤功能
REST_FRAMEWORK = { ... 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',) }
视图中使用
视图类中添加类属性filter_fields,指定可以过滤的字段
class StudentListView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentSerializer filter_fields = ('age', 'sex')
# 127.0.0.1:8000/four/students/?sex=1
排序
视图中使用
视图类中添加类属性filter_fields,指定可以过滤的字段
class StudentListView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_backends = [OrderingFilter] ordering_fields = ('id', 'age')
# 127.0.0.1:8000/books/?ordering=-id
# -id 表示针对id字段进行倒序排序
# id 表示针对id字段进行升序排序
如需过滤后再进行排序
需要局部配置过滤组件
from rest_framework.generics import ListAPIView from students.models import Student from .serializers import StudentModelSerializer from django_filters.rest_framework import DjangoFilterBackend class Student3ListView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer filter_fields = ('age', 'sex') # 因为局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明, # 否则过滤功能会失效 filter_backends = [OrderingFilter,DjangoFilterBackend] ordering_fields = ('id', 'age')
分页Pagination
分页
在settings中设置
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 100 # 每页数目 }
设置了全局分页
那么在drf中凡是调用了ListModelMixin的list(),都会自动分页
设置了全局分页的在视图中关闭分页能力
class 视图类(ListAPIView): pagination_class = None
自定义分页器
class LargeResultsSetPagination(PageNumberPagination): page_size = 1000 page_size_query_param = 'page_size' max_page_size = 10000 class BookDetailView(RetrieveAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer pagination_class = LargeResultsSetPagination
- page_size 每页数目
- page_query_param 前端发送的页数关键字名,默认为"page"
- page_size_query_param 前端发送的每页数目关键字名,默认为None
- max_page_size 前端最多能设置的每页数量
可选分页器
PageNumberPagination
前端访问网址形式
GET http://127.0.0.1:8000/students/?page=4
视图
# 声明分页的配置类 from rest_framework.pagination import PageNumberPagination class StandardPageNumberPagination(PageNumberPagination): # 默认每一页显示的数据量 page_size = 2 # 允许客户端通过get参数来控制每一页的数据量 page_size_query_param = "size" max_page_size = 10 # 自定义页码的参数名 page_query_param = "p" class StudentAPIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer pagination_class = StandardPageNumberPagination
LimitOffsetPagination
前端访问网址形式
GET http://127.0.0.1/four/students/?limit=100&offset=100
视图
from rest_framework.pagination import LimitOffsetPagination class StandardLimitOffsetPagination(LimitOffsetPagination): # 默认每一页查询的数据量,类似上面的page_size default_limit = 2 limit_query_param = "size" offset_query_param = "start" class StudentAPIView(ListAPIView): queryset = Student.objects.all() serializer_class = StudentModelSerializer # 调用页码分页类 # pagination_class = StandardPageNumberPagination # 调用查询偏移分页类 pagination_class = StandardLimitOffsetPagination
- default_limit 默认限制,默认值与 PAGE_SIZE 设置一致
- limit_query_param limit参数名,默认 limit
- offset_query_param offset参数名,默认 offset
- max_limit 最大limit限制,默认None
异常处理 Exceptions
自定义异常处理
独立的公共目录文件中编写
from rest_framework.views import exception_handler def custom_exception_handler(exc, context): # 先调用REST framework默认的异常处理方法获得标准错误响应对象 response = exception_handler(exc, context) # 在此处补充自定义的异常处理 if response is None: response.data['status_code'] = response.status_code return response
settings
在配置文件中声明自定义的异常处理
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler' }
如果未声明,会采用默认的方式
rest_frame/settings.py
REST_FRAMEWORK = { 'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler' }
REST framework定义的异常
APIException 所有异常的父类
ParseError 解析错误
AuthenticationFailed 认证失败
NotAuthenticated 尚未认证
PermissionDenied 权限决绝
NotFound 未找到
MethodNotAllowed 请求方式不支持
NotAcceptable 要获取的数据格式不支持
Throttled 超过限流次数
ValidationError 校验失败
自动生成接口文档
REST framework可以自动帮助我们生成接口文档
接口文档以网页的方式呈现
自动接口文档能生成的是继承自 APIView 及其子类的视图
安装依赖
pip install coreapi
设置接口文档访问路径
from rest_framework.documentation import include_docs_urls urlpatterns = [ ... path('docs/', include_docs_urls(title='站点页面标题')) ]
在settings.py中配置接口文档
REST_FRAMEWORK = { # 其他选项 # 接口文档 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema', }
在视图中给接口中进行注释
class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet): """ list: 返回图书列表数据 retrieve: 返回图书详情数据 latest: 返回最新的图书数据 read: 修改图书的阅读量 """
注释的说明
视图集ViewSet中的retrieve名称,在接口文档网站中叫做read
参数的Description需要在模型类或序列化器类的字段中以help_text选项定义
class Student(models.Model): ... age = models.IntegerField(default=0, verbose_name='年龄', help_text='年龄') ...
class StudentSerializer(serializers.ModelSerializer): class Meta: model = Student fields = "__all__" extra_kwargs = { 'age': { 'required': True, 'help_text': '年龄' } }
处理关于数据库的异常
from rest_framework.views import exception_handler as drf_exception_handler from rest_framework import status from django.db import DatabaseError def exception_handler(exc, context): response = drf_exception_handler(exc, context) if response is None: view = context['view'] if isinstance(exc, DatabaseError): print('[%s]: %s' % (view, exc)) response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE) return response