Django测试开发学习笔记(三)
restframework
restframework简介
它是基于Django的,帮助我们快速开发符合restful规范的接口框架,它主要适用于前后端分离项目。
文档1:https://www.django.cn/course/show-20.html
文档2:https://www.django-rest-framework.org/
快速入门
-
利用pycharm新建一个Django项目
image -
安装&配置Django REST framework
-
在虚拟环境中
imagepip install djangorestframework
-
settings.py文件中配置rest_framework
image
-
-
创建app
python manage.py startapp api
-
settings.py文件中配置app
image -
配置主路由 urls.py
image
-
创建模型
api/models.py
from django.db import models # Create your models here. class Project(models.Model): """ 项目表 """ ProjectType = ( ('Web', 'Web'), ('App', 'App') ) id = models.AutoField(primary_key=True) name = models.CharField(max_length=50, verbose_name='项目名称') version = models.CharField(max_length=50, verbose_name='版本') type = models.CharField(max_length=50, verbose_name='类型', choices=ProjectType) description = models.CharField(max_length=1024, blank=True, null=True, verbose_name='描述') status = models.BooleanField(default=True, verbose_name='状态') LastUpdateTime = models.DateTimeField(auto_now=True, verbose_name='最近修改时间') createTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') class Meta: db_table = "api_project"
-
settings.py配置数据库
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1', # 数据库主机 'PORT': 3306, # 数据库端口 'USER': 'root', # 数据库用户名 'PASSWORD': 'admin1234', # 数据库用户密码 'NAME': 'drf_demo' # 数据库名字 } }
-
创建序列化器Serializers
from rest_framework import serializers from api.models import Project class SerializersProject(serializers.ModelSerializer): class Meta: # 进行序列化的model model = Project # 展示的字段 fields = ["id","name","version","type","description","status","LastUpdateTime","createTime"]
-
数据库迁移
安装msyql client
pip install mysqlclient==1.4.6
数据库迁移
imagepython manage.py makemigrations python manage.py migrate api
-
视图
from rest_framework import viewsets # Create your views here. from api.models import Project from api.serializers import SerializersProject class Projects(viewsets.ModelViewSet): queryset = Project.objects.all() # 指定序列化器 serializer_class = SerializersProject
- 不再写多个视图,我们将所有常见行为分组写到叫 ViewSets 的类中。如果我们需要,我们可以轻松地将这些细节分解为单个视图,但是使用viewsets可以使视图逻辑组织良好,并且非常简洁。
-
配置子路由
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register(r'projects', views.Projects)
urlpatterns = [
]
urlpatterns += router.urls
- 因为我们使用的是viewsets而不是views,所以我们可以通过简单地使用路由器类注册视图来自动生成API的URL conf。再次,如果我们需要对API URL进行更多的控制,我们可以简单地将其拉出来使用常规基于类的视图,并明确地编写URL conf。最后,我们将包括用于支持浏览器浏览的API的默认登录和注销视图。这是可选的,但如果您的API需要身份验证,并且你想要使用支持浏览器浏览的API,那么它们很有用。
-
访问
imagehttp://127.0.0.1:8000/v01/
展示所有v01下的接口 -
访问
image imagehttp://127.0.0.1:8000/v01/projects/
序列化器
在开发REST API的视图中,虽然每个视图具体操作的数据不同,但增、删、改、查的实现流程基本套路化,所以这部分代码也是可以复用简化编写的:
增:校验请求数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回
删:判断要删除的数据是否存在 -> 执行数据库删除
改:判断要修改的数据是否存在 -> 校验请求的数据 -> 执行反序列化过程 -> 保存数据库 -> 将保存的对象序列化并返回
查:查询数据库 -> 将数据序列化并返回
Django REST framework(DRF) 提供了Serializer可以快速根据 Django ORM 或者其它库自动序列化/反序列化可以帮助我们简化上述部分代码编写,大大提高开发速度。
serializers
-
REST API接口核心任务
开发REST API接口时,常做的是三件事:
- 将请求的数据(如JSON格式)转换为模型类对象
- 操作数据库
- 将模型类对象转换为响应的数据(如JSON格式)
而数据格式化转换的操作往往是繁琐且重复的。
-
序列化Serialization
为了简化这些操作,引入新的功能:序列化和反序列化。
- 序列化
可将序列化的含义简单理解为:
将程序中的一个数据结构类型转换为其他格式(字典、JSON、XML等),例如将Django中的模型类对象装换为JSON字符串,这个转换过程我们称为序列化。
比如在一个查询数据的API中,我们查询数据库数据得到模型类对象,将其按照格式重组并转换为json的过程就是序列化:
from django.views.generic.base import View from .models import Project class ProjectList(View): def get(self,request): # 数据库查询获取模型类对象的QuerySet projects = Project.objects.all() # 序列化,把对象转换成字典 # 创建一个空列表,存储序每个项目描述字典 pro_list = [] for i in projects: pro = { "id":i.id, "name":i.name, "version":i.version, "type":i.type, "description":i.description, "status":i.status, "user_id":i.user.id } pro_list.append(pro) # 使用JsonResponse把python列表或者字典转换成json串并返回给前端,注意列表safe值必须为false return JsonResponse(pro_list,safe=False)
- 反序列化
相反的,可将反序列化的含义简单理解为:
将其他格式(字典、JSON、XML等)转换为程序中的数据,例如将JSON字符串转换为Django中的模型类对象,这个过程我们称为反序列化。
比如在一个新增数据的API中,我们接收前端传递来的json数据,将其转换为模型类对象的过程就是反序列化:
from django.views.generic.base import View from .models import Project class ProjectList(View): def post(self,request): # 获取到前端传过来的json串 json_data = request.body # 使用json.loads把json串转换成python字典或者列表 data = json.loads(json_data) # 反序列化 # 把python字典进行转换成模型类对象 pro = Project() pro.version = data["version"] pro.name = data["name"] pro.ProjectType = data["type"] pro.description=data["description"] pro.status=data["status"] pro.user = User.objects.filter(username=data["username"]).first() # 使用bulk_create方法把数据插入到数据库中 Project.objects.bulk_create([pro]) return HttpResponse("添加成功")
可以看出,在开发REST API接口时,我们在视图中需要做的最核心的事是:
- 将数据库数据序列化为前端所需要的格式,并返回;
- 将前端发送的数据反序列化为模型类对象,并保存到数据库中。
-
Serializer序列化器的使用
接下来我们使用restframework框架提供的序列化器来帮我们实现序列化和反序列化的操作。
Serializer类有两种,一种是基于Serializer类的基本序列化器,另一种是封装度更高,使用更简单的模型类ModelSerializer序列化器,其实为了效率,我们更多使用的是ModelSerializer类。
-
基于Serializer类的序列化器
- 首先新建一个serializers.py文件。
使用序列化基类实现Project模型类的序列化器:
在app中创建新文件serializers,在其内定义序列化器,继承于Serializer类。
image- 定义Serializer序列化器
# 导入模块 from rest_framework import serializers # 导入模型 from v02.models import Project # 继承Serializer类 class SerializersProject(serializers.Serializer): # 定义序列化器,字段须与模型中各个字段相同 # read_only只读 id = serializers.IntegerField(read_only=True) # required必填,min_length最小长度,max_length最大长度,先校验序列化器的,再校验模型定义的 name = serializers.CharField(required=True,min_length=4,max_length=40) # default默认值 version = serializers.CharField(max_length=50,default='v0.1') type = serializers.CharField(max_length=50,default='Web') # allow_blank允许为空,allow_null允许为null description = serializers.CharField(max_length=1024, allow_blank=True,allow_null=True) status = serializers.BooleanField(default=True) LastUpdateTime = serializers.DateTimeField(read_only=True) createTime = serializers.DateTimeField(read_only=True)
-
字段与参数
image -
序列化器使用
在视图中使用serializer对象直接构建序列化数据
# Create your views here. from v02.models import Project from v02.serializers import SerializersProject # 使用drf提供的基础视图APIView,继承自django的View class Projects(APIView): # 序列化器的使用代码示例 def get(self,request): projects = Project.objects.all() # 指定序列化器,instance表示实例,many=True表示格式为列表,不写表示格式为json serializer = SerializersProject(instance=projects,many=True) # drf提供的Response方法 return Response(serializer.data) # 反序列化器的使用代码示例 def post(self,request): data = request.data # 等同于 request.POST request.body serializer = SerializersProject(data=data) # is_valid按照定义的序列化器校验数据,raise_exception=True表示如果有问题时会抛出异常 serializer.is_valid(raise_exception=True) # 保存数据 serializer.save() # validated_data经过验证的数据 return Response(serializer.validated_data)
many参数:如果关联的对象数据不是只有一个,而是包含多个数据,需要使用many=True指定。
-
- 反序列化使用
```py
# 在视图中继续添加下面方法
# 反序列化器的使用代码示例
def post(self,request):
data = request.data # 等同于 request.POST request.body
serializer = SerializersProject(data=data)
# is_valid按照定义的序列化器校验数据,raise_exception=True表示如果有问题时会抛出异常
serializer.is_valid(raise_exception=True)
# 保存数据
serializer.save()
# validated_data经过验证的数据
return Response(serializer.validated_data)
```
![image](https://img.haomeiwen.com/i12041448/98a0008d9f706024.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
![image](https://img.haomeiwen.com/i12041448/8fd4c981df14a4e4.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
ModelSerializer
相比于Serializer类序列化器,DRF还提供了一个更加深度封装的ModelSerializer模型类序列化器,可以帮助我们更快的、傻瓜式的创建一个Serializer类。
-
ModelSerializer与常规的Serializer相同,但提供了:
- 基于模型类自动生成一系列字段(可以不再自己定义字段)
- 基于模型类自动为Serializer生成validators,比如unique_together()
- 包含默认的create()和update()的实现(可以不再自己定义update和create方法)
-
使用ModelSerializer定义Serializer序列化器
不再继承于serializers.Serializer类,而是继承模型序列化器类serializers.ModelSerializer,无需再自定义字段。
serializer.py
# 继承ModelSerializer类 class ModelSerializerProject(serializers.ModelSerializer): class Meta: # 指定要序列化的model model = Project # 指定要序列化的字段 fileds="__all__" # 所有字段 # fileds=[] 选择字段
view.py
把SerializersProject改为
serializers.ModelSerializerProject
,其他地方不变
image imagefrom rest_framework.response import Response from rest_framework.views import APIView from v02.models import Project from v02 import serializers class Projects(APIView): # 序列化器的使用 def get(self,request): projects = Project.objects.all() serializer = serializers.ModelSerializerProject(instance=projects,many=True) return Response(serializer.data) # 反序列化器的使用 def post(self,request): data = request.data serializer = serializers.ModelSerializerProject(data=data) serializer.is_valid(raise_exception=True) # 保存数据 serializer.save() # validated_data经过验证的数据 return Response(serializer.validated_data)
-
serializer和ModelSerializer的区别
image
序列化器常用操作
-
ModelSerializer元类属性
-
model指定数据模型
class ProjectModelSerializer(serializers.ModelSerializer): class Meta: model = Project # 指定要序列化的模型类,必填属性
-
fields 指定序列化字段
class ProjectModelSerializer(serializers.ModelSerializer): class Meta: model = Project # 指定要序列化的模型类,必填属性 fields = '__all__' # 指定要序列化显示的字段,和exclude属性必传一个,当然也可以共存
fields取值:
__all__
:表示显示模型类中的所有字段可以显示指定:
["id","name",…]
或者("id","name",…)
- exclude 指定非序列化字段
class ProjectModelSerializer(serializers.ModelSerializer): class Meta: model = Project # 指定要序列化的模型类,必填属性 fields = '__all__' # 指定要序列化显示的字段,必填属性 exclude = ['id'] # 指定不参与序列化的字段,同样也可以写成元祖,和fields属性必传一个,当然也可以共存
- read_only_fields指定只读字段
class ProjectModelSerializer(serializers.ModelSerializer): user_id = serializers.IntegerField(source="user.id") # 使用source指定, class Meta: model = Project fields = "__all__" read_only_fields = ['id','LastUpdateTime','createTime'] # 指定只读字段,也就是查询显示,不写入
- write_only_fields指定只写字段
class ProjectModelSerializer(serializers.ModelSerializer): user_id = serializers.IntegerField(source="user.id") # 使用source指定, class Meta: model = Project fields = "__all__" write_only_fields = ['user_id'] # 指定只写字段,也就是查询不显示,创建会写入数据库
- extra_kwargs给字段添加额外参数
imageclass ProjectModelSerializer(serializers.ModelSerializer): class Meta: model = Project fields = "__all__" read_only_fields = ['id','LastUpdateTime','createTime'] extra_kwargs = { # 使用extra_kwargs可以给字段添加额外的参数,而不用重新定义该字段 "name":{ "required":True, "max_length":40, "min_length":4, "error_messages":{ "required":"项目名必填", "max_length":"项目名不超过40位", "min_length":"项目名不少于4位" } } }
- depth 查询显示的深度
imageclass ProjectModelSerializer(serializers.ModelSerializer): class Meta: model = Project fields = "__all__" read_only_fields = ['id','LastUpdateTime','createTime'] extra_kwargs = { # 使用extra_kwargs可以给字段添加额外的参数,而不用重新定义该字段 "name":{ "required":True, } } depth = 1 # 定义查询嵌套的深度,最多不能超过10
-
-
自定义字段
可以指定模型中不存在的字段,例如查询时获取该项目下用例总数
imageclass ProjectModelSerializer(serializers.ModelSerializer): count = serializers.SerializerMethodField(label='用例数量',read_only=True) # 使用SerializerMethodField定义字段 def get_count(self,obj): ''' 为自定义字段赋值,方法名固定写法,get_字段名,obj代表当前表实例对象 :param obj: :return: ''' return 100
-
数据验证
- 使用extra_kwargs进行基本的验证
class ModelSerializerProject(serializers.ModelSerializer): class Meta: # 指定要序列化的model model = Project # 指定要序列化的字段 fields = '__all__'# 所有字段 extra_kwargs = { # 使用extra_kwargs可以给字段添加额外的参数,而不用重新定义该字段 "name": { "required": True, "max_length": 40, "min_length": 4, "error_messages": { "required": "项目名必填", "max_length": "项目名不超过40位", "min_length": "项目名不少于4位" } } }
-
Validation自定义验证逻辑(单字段)
class ModelSerializerProject(serializers.ModelSerializer): class Meta: # 指定要序列化的model model = Project # 指定要序列化的字段 fields = '__all__'# 所有字段 extra_kwargs = { # 使用extra_kwargs可以给字段添加额外的参数,而不用重新定义该字段 "name": { "required": True, "max_length": 40, "min_length": 4, "error_messages": { "required": "项目名必填", "max_length": "项目名不超过40位", "min_length": "项目名不少于4位" } } } # 对name字段自定义一些限制,使用"validate_限制字段()"函数来定义 def validate_name(self, value): # 只能输入字母、汉字、数字,这里如果限制了位数,可以把extra_kwargs对位数的限制去掉 r = r'^[\da-zA-Z_\u4e00-\u9fa5]{4,40}$' if re.match(r,value): return value else: # 抛异常 raise serializers.ValidationError("项目名称为4-40位字母或汉字")
不符合正则:
image符合正则:
image
- Validation自定义验证逻辑(多字段)
```py
# 继承ModelSerializer类
class ModelSerializerProject(serializers.ModelSerializer):
# 新增两个字段,用来讲解validate多字段验证
startTime = serializers.IntegerField()
endTime = serializers.IntegerField()
class Meta:
# 指定要序列化的model
model = Project
# 指定要序列化的字段
fields = '__all__' # 所有字段
# fileds=[] 选择字段
extra_kwargs = { # 使用extra_kwargs可以给字段添加额外的参数,而不用重新定义该字段
"name": {
"required": True,
"max_length": 40,
"min_length": 4,
"error_messages": {
"required": "项目名必填",
"max_length": "项目名不超过40位",
"min_length": "项目名不少于4位"
}
}
}
def validate_name(self, value):
# 只能输入字母和汉字
r = r'^[\da-zA-Z_\u4e00-\u9fa5]{4,40}$'
if re.match(r, value):
return value
else:
raise serializers.ValidationError("项目名称为4-40位字母或汉字")
# 多字段验证
def validate(self, attrs):
print("attrs:{}".format(attrs))
startTime = attrs.pop("startTime")
endTime = attrs.pop("endTime")
if endTime > startTime:
return attrs
else:
raise serializers.ValidationError("结束时间必须大于开始时间")
```
结束时间大于开始时间:
![image](https://img.haomeiwen.com/i12041448/f44238c7fa4047ae.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
结束时间小于开始时间:
![image](https://img.haomeiwen.com/i12041448/d3cd5fe91257b578.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
-
关联查询
使用ORM多表关联时的学习模型:
# 为了讲解关联查询新增的model class Stu(models.Model): s_name = models.CharField('学生姓名', max_length=64, null=False, help_text='学生姓名') s_sex = models.IntegerField('性别', max_length=1, choices=((0, '男'), (1, '女')), help_text='性别') s_age = models.PositiveIntegerField('年龄', null=False, default=0, help_text='年龄') s_dept = models.CharField("专业", max_length=64, null=False, help_text="专业") si_id = models.OneToOneField(to='StudentInfo', to_field="id", on_delete=models.CASCADE, db_column='si_id', related_name="stu_stu_info") course = models.ManyToManyField(to="Course", db_table="stu_course", related_name="course_student") class Meta: db_table = 'stu' # 学生信息表 class StudentInfo(models.Model): s_card = models.CharField("身份证号", max_length=18, null=False, unique=True) s_phone = models.CharField(max_length=11, null=False, unique=True, help_text="手机号") s_addr = models.CharField("家庭住址", max_length=128, help_text="家庭住址") class Meta: db_table = "student_info" # 课程表 class Course(models.Model): c_name = models.CharField('课程名', max_length=64, null=False, help_text="课程名") t_id = models.ForeignKey(to="Teacher", to_field="id", on_delete=models.CASCADE, related_name='teacher_course', db_column='t_id', help_text="老师编号") class Meta: db_table = "course" # 成绩表 class Score(models.Model): s_id = models.ForeignKey(to="Stu", to_field="id", on_delete=models.CASCADE, related_name='stu_score', db_column='s_id', help_text="学生编号") c_id = models.ForeignKey(to="Course", to_field="id", on_delete=models.CASCADE, related_name='course_score', db_column='c_id', help_text="学生编号") score = models.PositiveIntegerField("成绩", null=False, default=0, help_text="成绩") class Meta: db_table = "score" # 老师表 class Teacher(models.Model): t_name = models.CharField("老师姓名", max_length=64, null=False, help_text="老师姓名") class Meta: db_table = "teacher"
- 一对一 (学生表和学生信息表)
serializers.py
# 每个model都要创建序列化器 class ModelSerializerStu(serializers.ModelSerializer): phone = serializers.StringRelatedField(source="si_id.s_phone") class Meta: model = Stu fields = "__all__" class ModelSerializerStudentInfo(serializers.ModelSerializer): # 没有调试成功,GG name = serializers.StringRelatedField(source="stu_stu_info.s_phone") class Meta: model = StudentInfo fields = "__all__"
urls.py
imageurlpatterns = [ path('projects/',views.Projects.as_view()), path('students/',views.Students.as_view()) ]
-
多对多 (学生表和课程表)
serializers.py
imageclass ModelSerializerStu(serializers.ModelSerializer): phone = serializers.StringRelatedField(source="si_id.s_phone") # drf的序列化器中没有直接多对多的关系的序列化操作 # 使用SerializerMethodField自定义字段来实现 course_name = serializers.SerializerMethodField(read_only=True) class Meta: model = Stu fields = "__all__" def get_course_name(self, obj): # 这个学生的所有课程 courses = obj.course.all() # 返回课程名列表 return [c.c_name for c in courses]
子序列化 (没懂)
-
了解
- 只能在序列化中使用
- 字段名必须是外键字段
- 相对于自定义序列化外键字段,自定义序列化字段是不能参与反序列化的,而子序列化必须为外键名,所以就无法入库
- 在外键关联数据是多余时,需要明确many=True
- 子序列化是单向操作,因为作为子序列的类必须写在上方,所以不能产生逆方向的子序列化
-
实例
from rest_framework import serializers from . import models class ModelSerializerStudent(serializers.ModelSerializer): phone = serializers.StringRelatedField(source="si_id.s_phone")# 正向,根据属性名查询 class Meta: model=models.Stu fields="__all__" class ModelSerializerCourse(serializers.ModelSerializer): course_student = ModelSerializerStudent(many=True,read_only=True) class Meta: model = models.Course fields = "__all__"
view视图
APIView
APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
APIView与View不同之处在于:
-
传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
-
视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
-
任何APIException异常都会被捕获到,并且处理成合适的响应信息;
-
在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
-
支持定义的属性:
- authentication_classes 列表或元祖,身份认证类
- permissoin_classes 列表或元祖,权限检查类
- throttle_classes 列表或元祖,流量控制类
-
在APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。
用户认证
-
drf自带的TokenAuthentication
- 配置rest_framework.authtoken
settings.py中INSTALLED_APPS中添加应用
'rest_framework.authtoken'
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'api', 'v02', 'rest_framework.authtoken', ]
- 数据迁移生成token表
python manage.py migrate
注意,注意一定不要执行``python manage.py makemigrations`
image-
配置token认证类
settings.py
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer',) ,# 浏览API渲染器 ) # 自定义异常函数 'EXCEPTION_HANDLER':'utils.custom_exception.exception_handler', # 认证类配置 'DEFAULT_AUTHENTICATION_CLASSES':[ 'rest_framework.authentication.TokenAuthentication' ], }
-
定义登录和注册方法
views.pyfrom django.contrib.auth import authenticate from django.contrib.auth.models import User # Create your views here. from rest_framework.authtoken.models import Token from rest_framework.response import Response from rest_framework.views import APIView class Signup(APIView): authentication_classes = [] # 去掉用户认证 permission_classes = [] # 去掉权限认证 def post(self,request): data = request.data user = User.objects.create_user(**data) # 创建用户 user.save() token, created = Token.objects.update_or_create(user=user) # 创建token ret = {} ret["code"] = '0000' ret["msg"] = '注册成功' ret['data'] = { "id":user.id, "username":user.username, "password": user.password, "token":token.key } return Response(ret) class Login(APIView): authentication_classes = [] # 去掉用户认证 permission_classes = [] # 去掉权限认证 def post(self,request): data = request.data res = authenticate(**data) # 验证用户名和密码 ret = {} if res: token,created = Token.objects.update_or_create(user=res) # 创建token print(token) ret["code"] = '0000' ret["msg"] = '登录成功' ret['data'] = token.key else: ret["code"] = '9999' ret["msg"] = '登录失败' ret['data'] = None return Response(ret) # 用于测试 class TestLogin(APIView): def get(self,request): return Response("{}".format(request.user))
没带token v02/test/接口:
image注册接口获得token
image数据库token表存入token
image使用该token访问v02/test/接口:
image
-
自定义身份认证类
utils目录下
custom_authentication.py
# 0 游客 # 1 普通会员 # ... from django.contrib.auth.models import User from rest_framework.authentication import BaseAuthentication class MyAuthentication(BaseAuthentication): """ 一般重新定义用户模型和token管理方案,这个类必须重写 """ def authenticate(self, request): # 获取请求头中的token,如果传Authorization,就使用HTTP_Authorization全大写 auth = request.META.get('HTTP_TOKEN', None) # 如果auth为空,表示未登录 if not auth: # return None, None 返回一个user和token为空 return 0,None # 0代表游客,可以自定义 # 对token进行切割,如果不是2段,且第一段为token,同样表示未登录 auth_list = auth.split() if not (len(auth_list) == 2 and auth_list[0] == 'token'): return None, None key = auth_list[1] try: user = User.objects.select_releted("auth_token").get(auth_token_key=key) except: return None, None return user, auth # 不用写内容,但是不写该函数会报错 def authenticate_header(self, request): pass
权限控制
-
drf默认提供了一些权限类
- rest_framework.permissions.AllowAny:游客和登录用户有全权限
- rest_framework.permissions.IsAuthenticated:只有登录用户有全权限
- rest_framework.permissions.IsAdminUser:只有后台用户(admin用户)有全权限
- rest_framework.permissions.IsAuthenticatedOrReadOnly:游客有读权限,登录用户有全权限
-
配置权限类
-
全局配置
settings.py
注意:要去掉自定义认证,使用drf默认的认证配置
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer',), # 浏览API渲染器 ) # 自定义异常函数 'EXCEPTION_HANDLER': 'utils.custom_exception.exception_handler', # 认证类配置 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication' ], # 权限类配置 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', # 配置为:只有登录用户有全权限 ], }
-
局部配置
views.py
class TestLogin(APIView): permission_classes = [AllowAny] # 局部配置权限认证,表示游客和登录用户有全权限 def get(self, request): return Response("{}".format(request.user))
注释掉局部配置、注释掉请求头的token
image有局部配置,没有token,表示没有token是游客也能访问到
image
-
-
自定义权限类
需要自定义两个方法:
def has_permission(self, request, view): def has_object_permission(self, request, view, obj):
# 导入权限基类 from rest_framework.permissions import BasePermission class LoginPermission(BasePermission): def has_permission(self, request, view): user = request.user # 在自定义身份认证中将0定义为游客身份 if user == 0: return False else: return True # 可以通过定义不同的方法,给不同权限的人身份认证, def vip_permission(self, request, view): user = request.user # 在自定义身份认证中将0定义为游客身份 if user >= 1: return False else: return True # obj为自定义身份认证中的user,一般使用django原生的用户认证才使用 # def has_object_permission(self, request, view, obj): # obj.user = request.user # pass
views.py
class TestLogin(APIView): # 权限认证和身份认证都该为自定义配置 permission_classes = [LoginPermission] authentication_classes = [MyAuthentication] def get(self, request): return Response("{}".format(request.user))
流量控制
节流又叫限流,限制访问。就是通常一个用户在多次请求一个页面,或者点击一个链接的时候,前几次点击是没有问题的,但是一旦连续几次之后,就会出现访问受限,离下一次访问还有50秒等的字样,在django rest framework 中有一个专门的组件来做限制访问。 例如:手机接收验证码
-
drf默认频率类
-
可选频率类
- rest_framework.throttling.AnonRateThrottle : 限制所有匿名未认证用户,使用IP区分用户
- rest_framework.throttling.UserRateThrottle : 限制认证用户,使用User id 来区分。
- rest_framework.throttling.ScopedRateThrottle : 限制用户对于每个视图的访问频次,使用ip或user id
-
配置频率类(对于上述用户认证的频率配置)
-
全局配置
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer',), # 浏览API渲染器 ) # 自定义异常函数 'EXCEPTION_HANDLER': 'utils.custom_exception.exception_handler', # 认证类配置 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication' ], # 权限类配置 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', # 只有登录用户有全权限 ], "DEFAULT_THROTTLE_CLASSES":["rest_framework.throttling.AnonRateThrottle",'rest_framework.throttling.UserRateThrottle'], # 全局配置频率类 "DEFAULT_THROTTLE_RATES":{ "anon":'5/m', # 对应的key设置访问频率 "user":'10/m', # 对应的key设置访问频率 } }
views.py
class TestLogin(APIView): permission_classes = [AllowAny] def get(self, request): return Response("{}".format(request.user))
-
-
未登录用户访问5次后,效果:
![image](https://img.haomeiwen.com/i12041448/8edb4cf73c26df69.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
已登录用户访问10次,效果:
![image](https://img.haomeiwen.com/i12041448/9b98d101416d3277.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 局部配置(在视图中进行配置)
```py
# 导入自定义频率
from rest_framework.throttling import AnonRateThrottle
from rest_framework.throttling import UserRateThrottl
class TestLogin(APIView):
throttle_classes =[AnonRateThrottle,UserRateThrottle]
def get(self, request):
return Response("{}".format(request.user))
```
-
使用ScopedRateThrottle来自定义频率
替换原来的settings.py频率配置部分
REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES":["rest_framework.throttling.ScopedRateThrottle"], # 全局配置频率类 "DEFAULT_THROTTLE_RATES":{ 'five':'5/m', # 自定义频率字段 'eight':'8/m' } }
视图中使用throttle_scope来使用自定义频率
class TestLogin(APIView): permission_classes = [AllowAny] throttle_scope='eight' # 使用throttle_scope来使用自定义频率 def get(self, request): return Response("{}".format(request.user))
- 自定义频率类(需要再看吧,跳过)
请求和响应
-
Request类
对一个APIView的子类, 重写get, post, put等方法就相当于解析这个路径的get, post, put请求,
请求对象就是request对象, http header body 的内容都被包含在request对象中.
request对象的类来自 from rest_framework.request import Request判断对象是否是某个类实例化而来
-
from rest_framework.request import Request
-
if isinstance(request, Request)
下面分别分析不同情况的参数位置和类型, 最终写出一个方法能够将任何类型的请求参数统一转换为dict方便之后的逻辑编写。
-
GET
get请求中参数都会以
http://xxx.com/api/getjson?param1=asdf&m2=123
这样的形式拼接在url后面。在request对象中
-
request.query_params 中可以获取?param1=32¶m2=23形式的参数.
-
request.query_params 返回的数据类型为QueryDict
-
QueryDict转为普通python字典. query_params.dict()即可。
-
-
POST
post请求参数都在请求体中, 但是其实你的url可以写成get的形式, 最终结果, 参数会有两部分组成, 一部分在url中, 一部分在http body 中, 但是非常不建议这样做。
接下来的代码编写也不会考虑这样的情况, post 仅考虑所有参数都在http body 中的情况。
image
-
-
PUT
image -
PATCH
![image](https://img.haomeiwen.com/i12041448/8e942ba2c2f4b5c3.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
-
DELETE
image -
编写参数统一处理的方法
from django.http import QueryDict
from rest_framework.request import Request
def get_parameter_dic(request, *args, **kwargs):
if isinstance(request, Request) == False:
return {}
query_params = request.query_params
if isinstance(query_params, QueryDict):
query_params = query_params.dict()
result_data = request.data
if isinstance(result_data, QueryDict):
result_data = result_data.dict()
if query_params != {}:
return query_params
else:
return result_data
-
Response类
REST framework提供了一个响应类Response,使用该类构造响应对象时,响应的具体数据内容会被转换(render渲染)成符合前端需求的类型。
REST framework提供了Renderer 渲染器,用来根据请求头中的Accept(接收数据类型声明)来自动转换响应数据到对应格式。如果前端请求中未进行Accept声明,则会采用默认方式处理响应数据,我们可以通过配置来修改默认响应格式。
在settings.py中加入:
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会根据前端所需类型数据来设置该参数。
-
1).data :传给response对象的序列化后,但尚未render处理的数据
-
2).status_code:状态码的数字
-
3).content:经过render处理后的响应数据,django在报错的时候,会直接在浏览器暴露报错信息,所以我们可以通过重写异常处理函数来处理服务器的报错信息,重写Response类格式化响应输出信息
-
-
重写Response类
utils文件夹下新增custom_response.py
```py
from rest_framework.response import Response
# 自定义一个响应,继承自Response类
class CustomResponse(Response):
def __init__(self, *args, code='0000', msg="成功", **kwargs):
# 格式化data
data = {
"code": code,
"message": msg
}
if args is not None:
data["data"] = args[0]
kwargs["data"] = data
elif "data" in kwargs:
data["data"] = kwargs["data"]
kwargs["data"] = data
super().__init__(**kwargs)
```
views.py下修改:
![image](https://img.haomeiwen.com/i12041448/7b888e74144989c9.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
效果如下:
![image](https://img.haomeiwen.com/i12041448/75c6adc034fa1b39.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
-
重写异常类
utils文件夹下新增custom_exception.py
from rest_framework import status from rest_framework.exceptions import ValidationError from rest_framework.views import exception_handler as drf_exception_handler from utils.custom_response import CustomResponse def exception_handler(exc, context): """ 自定义异常处理 :param exc: 别的地方抛的异常就会传给exc :param context: 字典形式。抛出异常的上下文(即抛出异常的出处;即抛出异常的视图) :return: Response响应对象 """ response = drf_exception_handler(exc, context) if response is None: # drf 处理不了的异常 print('%s - %s - %s' % (context['view'], context['request'].method, exc)) return CustomResponse({'detail': '服务器错误'}, code=500, msg="服务器内部错误", status=status.HTTP_500_INTERNAL_SERVER_ERROR, exception=True) if isinstance(exc, ValidationError): message = "" data = response.data for key in data: message += ";".join(data[key]) return CustomResponse(None, code="9999", msg=message) return response
settings.py新增指定异常处理函数
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': ( # 默认响应渲染类 'rest_framework.renderers.JSONRenderer', # json渲染器 'rest_framework.renderers.BrowsableAPIRenderer',) ,# 浏览API渲染器 ) # 自定义异常函数 'EXCEPTION_HANDLER':'utils.custom_exception.exception_handler', }
效果:
image
GenericAPIView
rest_framework.generics.GenericAPIView,继承自APIVIew,增加了对于列表视图和详情视图可能用到的通用支持方法。通常使用时,可搭配一个或多个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)
常用,返回序列化器对象,被其他视图或扩展类使用,如果我们在视图中想要获取序列化器对象,可以直接调用此方法。
-
实例:
注释掉setiings.py中的认证配置和权限配置,以便我们后面讲解方便。
注释掉之前为了讲解字段验证新增的startTime、endTime代码。修改原来的Projects
imagefrom rest_framework.generics import GenericAPIView # 两大属性 # 查询集 queryset = Project.objects.all() # 序列化器 serializer_class = serializers.ModelSerializerProject # 序列化器的使用 def get(self, request): # 获取查询集,等同于projects = Project.objects.all() projects =self.get_queryset() # 进行序列化,等同于serializer = serializers.ModelSerializerProject(instance=projects, many=True) serializer = self.get_serializer(instance=projects,many=True) return Response(serializer.data)
-
-
使用django-filter实现过滤查询
-
安装django-filter
pip install django-filter
-
添加至INSTALLED_APPS
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'api', 'v02', 'rest_framework.authtoken', 'django_filters', ]
-
设置过滤后端
对单个GenericAPIView视图设置过滤器后端
class Projects(GenericAPIView): queryset = Project.objects.all() serializer_class = serializers.ModelSerializerProject # 针对某个GenericAPIView视图添加过滤 filter_backends = [django_filters.rest_framework.DjangoFilterBackend] def get(self, request): projects = self.get_queryset() serializer = self.get_serializer(instance=projects, many=True) return Response(serializer.data)
我们还可以在settings.py中添加全局设置默认过滤器后端,可以通过、
DEFAULT_FILTER_BACKENDS
设置通用的过滤后端# rest_framework框架全局设置 REST_FRAMEWORK = { "DEFAULT_FILTER_BACKENDS":[# 设置全局引擎 'django_filters.rest_framework.DjangoFilterBackend', # 设置后端过滤 ] }
- 基本使用
# 使用GenericAPIView class Projects(GenericAPIView): # 两大属性 # 查询集 queryset = Project.objects.all() # 序列化器 serializer_class = serializers.ModelSerializerProject # 针对某个GenericAPIView视图添加过滤 filter_backends = [django_filters.rest_framework.DjangoFilterBackend] filterset_fields=['name','type'] # 序列化器的使用 def get(self, request): projects = self.get_queryset() # 使用filter_queryset方法对查询集进行过滤 serializer = self.get_serializer(instance=self.filter_queryset(projects), many=True) return Response(serializer.data)
过滤出name=hello测开的数据:
image -
-
重写FilterSet类
我们会发现,查询参数的名字太长了并且也不支持模糊查询,我们可以通过重写FilterSet类来实现自定义查询参数的字段名和查询规则。
- 在想自定义查询类的应用下新建
custom_filters.py
,不是放在项目文件utils下,因为不是公共的,每个应用都不同的查询类
- 在想自定义查询类的应用下新建
```py
from django_filters import rest_framework as filters
from . import models
class ProjectFilter(filters.FilterSet):
# field_name要过滤的字段 lookup_expr要过滤的规则
name = filters.CharFilter(field_name="name", lookup_expr='contains') # 对名字进行模糊查询
ver = filters.CharFilter(field_name="version") # 字段可以自己重新命名,对version进行精确查询
class Meta:
model = models.Project
fields = ['name','ver'] # 指定查询参数
```
- 自定义过滤字段和模型对照表
![image](https://img.haomeiwen.com/i12041448/45475a648e36ad55.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 视图中指定查询类
```py
# 使用GenericAPIView
class Projects(GenericAPIView):
queryset = Project.objects.all()
serializer_class = serializers.ModelSerializerProject
# 针对某个GenericAPIView视图添加过滤
filter_backends = [django_filters.rest_framework.DjangoFilterBackend]
# 指定查询类
filterset_class = ProjectFilter
def get(self, request):
projects = self.get_queryset()
# 使用filter_queryset方法对查询集进行过滤
serializer = self.get_serializer(instance=self.filter_queryset(projects), many=True)
return Response(serializer.data)
```
![image](https://img.haomeiwen.com/i12041448/04850db0ec269c3f.image?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
-
排序
- 设置排序的backends
全局配置,settings.py
# rest_framework框架全局设置 REST_FRAMEWORK = { "DEFAULT_FILTER_BACKENDS":[# 设置全局引擎 'rest_framework.filters.OrderingFilter', # 排序引擎 ] }
单个视图中设置
from rest_framework import filters class Projects(GenericAPIView): queryset = Project.objects.all() serializer_class = serializers.ModelSerializerProject # 针对某个GenericAPIView视图添加过滤,filters.OrderingFilter视图中设置排序引擎 filter_backends = [django_filters.rest_framework.DjangoFilterBackend,filters.OrderingFilter] filterset_class = ProjectFilter def get(self, request): projects = self.get_queryset() serializer = self.get_serializer(instance=self.filter_queryset(projects), many=True) return Response(serializer.data)
- 指定排序字段
使用ordering_fields属性显示指定要排序的字段,不指定则默认序列化器上的任何可读字段都可用来排序
class Projects(GenericAPIView): queryset = Project.objects.all() serializer_class = serializers.ModelSerializerProject # 针对某个GenericAPIView视图添加过滤,filters.OrderingFilter视图中设置排序引擎 filter_backends = [django_filters.rest_framework.DjangoFilterBackend,filters.OrderingFilter] filterset_class = ProjectFilter # 指定排序字段 ordering_fields = ['id'] def get(self, request): projects = self.get_queryset() serializer = self.get_serializer(instance=self.filter_queryset(projects), many=True) return Response(serializer.data)
需要使用
imageordering=id
或ordering=-id
字段来指定排序规则- 指定排序默认字段
使用ordering属性指定默认排序字段
class Projects(GenericAPIView): queryset = Project.objects.all() serializer_class = serializers.ModelSerializerProject # 针对某个GenericAPIView视图添加过滤,filters.OrderingFilter视图中设置排序引擎 filter_backends = [django_filters.rest_framework.DjangoFilterBackend,filters.OrderingFilter] filterset_class = ProjectFilter # 指定排序字段 ordering_fields = ['id'] # 指定默认排序字段,倒序 ordering = ['-id'] def get(self, request): projects = self.get_queryset() serializer = self.get_serializer(instance=self.filter_queryset(projects), many=True) return Response(serializer.data)
此时,不用在url中指定ordering,就可以默认按id倒序排列
image -
分页
我们数据库有几千万条数据,这些数据需要展示,我们不可能直接从数据库把数据全部读取出来.
因为这样会给内存造成巨大的压力,很容易就会内存溢出,所以我们希望一点一点的取。同样,展示的时候也是一样的,我们必定会对数据进行分页显示。
- 普通分页 查第n页,每页显示n条数据
1)分页器配置文件
应用下新建custom_paginations.py
from rest_framework import pagination
class PageNumberPagination(pagination.PageNumberPagination):
"""查第n页,每页显示n条数据"""
page_size = 1 # 指定每页默认显示多少条数据
page_size_query_param = 'size' # URL参数中每页显示条数的参数
page_query_param = 'page' # URL中页码的参数
max_page_size = 40 # 每页最多显示多少条数据
2)视图函数使用分页器
class Projects(GenericAPIView):
queryset = Project.objects.all()
serializer_class = serializers.ModelSerializerProject
filter_backends = [django_filters.rest_framework.DjangoFilterBackend,filters.OrderingFilter]
filterset_class = ProjectFilter
ordering_fields = ['id']
# 指定默认排序字段
ordering = ['-id']
# 1. 指定分页器对象
pagination_class = PageNumberPagination
def get(self, request):
# 在过滤后端下,这些写
projects = self.filter_queryset(self.get_queryset())
# 2. 使用自己配置的分页器调用分页方法进行分页
page_obj = self.paginate_queryset(projects)
# 3. 序列化我们分页好的数据
serializer = self.get_serializer(instance=page_obj, many=True)
# 4. 返回带上一页/下一页连接的页面
return self.get_paginated_response(serializer.data)
效果:可以在过滤下分页,选择第几页,每页展示多少条数据
image前端展示分页时,可以直接调用上一页链接和下一页链接
image-
偏离分页 在第n个位置,向后查n条数据 (示例代码,没跑过,需要时,抄代码)
1)分页器配置文件
from rest_framework import pagination class LimitOffsetPagination(pagination.LimitOffsetPagination): """在第n个位置,向后查n条数据""" default_limit = 1 # 指定默认查多少条数据 limit_query_param = 'limit' # URL中指定查多少条数据的参数 offset_query_param = 'offset' # URL中指定从第几条数据开始查的参数 max_limit = 999 # 最大显示多少条数据
2)视图函数使用分页器
from v03 import pagination class Students(GenericAPIView,ListModelMixin): queryset = Stu.objects.all() serializer_class = ModelSerializerStudent pagination_class = pagination.LimitOffsetPagination # 1. 指定分页器对象 def get(self,request,*args,**kwargs): students = self.get_queryset() # 2. 使用自己配置的分页器调用分页方法进行分页 page_obj = self.paginate_queryset(students) # 3.序列化我们分页好的数据 serializer = self.get_serializer(instance=page_obj,many=True) # 4. 返回数据 # return Response(serializer.data) # 4. 返回带上一页/下一页连接的页面 return self.get_paginated_response(serializer.data)
-
加密分页 (示例代码,没跑过,需要时,抄代码)
1)分页器配置文件
class CursorPagination(pagination.CursorPagination): """加密游标的分页""" cursor_query_param = 'cursor' # 游标(这是加密的游标) # ordering = '-id' # 从后往前取数据 ordering = 'id' # 从前往后取数据 page_size = 1 # 每页显示的条数
2)视图函数使用分页器
from v03 import pagination class Students(GenericAPIView,ListModelMixin): queryset = Stu.objects.all() serializer_class = ModelSerializerStudent pagination_class = pagination.CursorPagination # 1. 指定分页器对象 def get(self,request,*args,**kwargs): students = self.get_queryset() # 2. 使用自己配置的分页器调用分页方法进行分页 page_obj = self.paginate_queryset(students) # 3.序列化我们分页好的数据 serializer = self.get_serializer(instance=page_obj,many=True) # 4. 返回数据 # return Response(serializer.data) # 4. 返回带上一页/下一页连接的页面 return self.get_paginated_response(serializer.data)
View、APIView、GenericAPIView的区别
https://www.cnblogs.com/hanbowen/p/9885358.html
mixins类
image和GenericAPIView一同搭配使用的还有五个扩展类,他们分别是ListModelMixin, CreateModelMixin,UpdataModelMixin,RetrieveModelMixin,DestroyModelMixin
mixin 混合类,不能单独使用,需要利用python支持多继承结合GenericAPIView一起用
class Projects(ListModelMixin,GenericAPIView):
queryset = Project.objects.all()
serializer_class = serializers.ModelSerializerProject
filter_backends = [django_filters.rest_framework.DjangoFilterBackend, filters.OrderingFilter]
filterset_class = ProjectFilter
ordering_fields = ['id']
ordering = ['-id']
def get(self, request,*args,**kwargs):
return self.list(request,*args,**kwargs)
-
ListModelMixin(查询所有)
提供了
list(request,*args,**kwargs)
方法快速实现列表视图,返回的是状态码是默认的200 -
CreateModelMixin(创建对象)
创建视图的扩展类,提供类
create(request,*args,**kwargs)
方法快速实现创建资源的视图,成功后返回201状态码如果序列化器对前端发送的数据验证失败,则返回400的错误
-
UpdataModelMixin(更新视图)
更新视图扩展类,提供
update(request, *args, **kwargs)
方法,可以快速实现更新一个存在的数据对象。也同时提供
partial_update(request, *args, **kwargs)
方法,可以实现局部更新。成功返回200,序列化器校验数据失败时,返回400错误。
-
RetrieveModelMixin(查询单个对象详情)
详情视图扩展类(即查询单个对象),提供了类retrieve方法
可以快速实现返回一个存在的数据对象
如果存在,返回200,否则就返回404
-
DestroyModelMixin(删除视图)
删除视图扩展类,提供
destroy(request,*args,**kwargs)
方法,可以快速实现删除一个存在的数据对象
GenericAPIView+mixins
继承这些类时,要重写下面的方法。
image image image image imageViewSet的使用
-
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:
-
list() 提供一组数据
-
retrieve() 提供单个数据
-
create() 创建数据
-
update() 保存数据
-
destory() 删除数据
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。
-
-
action属性
在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个
-
常用视图集父类
1) ViewSet
继承自APIView,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
2)GenericViewSet
继承自GenericAPIView,作用也与GenericAPIVIew类似,提供了get_object、get_queryset等方法便于列表视图与详情信息视图的开发。
3)ModelViewSet
继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
4)ReadOnlyModelViewSet
继承自GenericAPIVIew,同时包括了ListModelMixin、RetrieveModelMixin。
-
GenericViewSet例子
views.py
# 使用GenericViewSet class Projects(ListModelMixin,GenericViewSet): queryset = Project.objects.all() serializer_class = serializers.ModelSerializerProject filter_backends = [django_filters.rest_framework.DjangoFilterBackend, filters.OrderingFilter] filterset_class = ProjectFilter ordering_fields = ['id'] ordering = ['-id']
路由配置 urls.py,
# 路由配置中显示指定请求方法对应的视图 urlpatterns = [ path('projects/',views.Projects.as_view({'get': 'list'})) ]
-
ModelViewSet
ModelViewSet继承了下图这些类,所以我们在使用ModelViewSet时,不用再加继承ListModelMixin等。
imageviews.py
# 使用ModelViewSet
class Projects(ModelViewSet):
queryset = Project.objects.all()
serializer_class = serializers.ModelSerializerProject
filter_backends = [django_filters.rest_framework.DjangoFilterBackend, filters.OrderingFilter]
filterset_class = ProjectFilter
ordering_fields = ['id']
ordering = ['-id']
# 路由配置中显示指定请求方法对应的视图
urlpatterns = [
path('projects/',views.Projects.as_view({'get': 'list','post':'create'}))
]
image
路由控制
-
自定义路由(原始方式) (用到再看)
-
半自动路由(视图类集成ModelViewSet)(用到再看)
-
全自动路由(自动生成路由)
urls.py
imagerouter = routers.DefaultRouter() # 获取一个routers实例 router.register("projects", views.Projects)# 注册路由信息,两个参数:一个是匹配路由,一个是视图类 urlpatterns = [ ] urlpatterns += router.urls
view视图总结
-
DRF中所有的视图文件
from rest_framework import views # APIView from rest_framework import generics # 公共通用视图类:GenericAPIView,及各种组合视图类CreateAPIView、ListAPIView、RetrieveAPIView等 from rest_framework import mixins # 混合继承类:CreateModelMixin、ListModelMixin、RetrieveModelMixin、UpdateModelMixin、DestroyModelMixin from rest_framework import viewsets # 重写as_view: ViewSetMixin;其他类都是帮助去继承ViewSetMixin
-
DRF中的视图图谱
首先 django是继承 view的,DRF是从APIView开始继承起,APIView封装了request,其中包含了data、query_params等属性、方法。
然后 GenericAPIView封装了 get_queryset() 和 get_serializer();ViewSetMixin重写了 as_view()方法。
随后 GenericViewSet帮忙继承GenericAPIView和ViewSetMixin。
最后最高层的封装是 ModelViewSet。
image