大家好,我是景天,今天我们继续探讨DRF,Django的web框架Django Rest_Framework(三)
1.DRF视图
Django REST framwork 提供的视图的主要作用:
- 控制序列化器的执行(检验、保存、转换数据)
- 控制数据库查询的执行
REST framework 提供了众多的通用视图基类与扩展类,以简化视图的编写。
1) 视图基类
APIView:
rest_framework.views.APIView
APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
APIView与View的不同之处在于:
- 传入到视图方法中的是REST framework的
Request
对象,而不是Django的HttpRequeset
对象; - 视图方法可以返回REST framework的
Response
对象,视图会为响应数据设置(render)符合前端要求的格式;
- 任何
APIException
异常都会被捕获到,并且处理成合适的响应信息; - 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
创建个应用来验证视图
序列化器
主要是视图类,使用了DRF的Response 返回页面
1.GET请求
from django.shortcuts import render # Create your views here. from ser import models ##使用DRF提供的APIView from rest_framework.views import APIView #使用DRF提供的Response响应数据 from rest_framework.response import Response from req.MySerializers import StudentSerializer class StudentView(APIView): def get(self,request): studentdata = models.Student.objects.all() ser_obj = StudentSerializer(instance=studentdata,many=True) return Response(ser_obj.data) #不需要加JsonResponse的一些适配参数比如safe=False等
页面是由rest_framework封装的Response返回的页面,浏览器访问是个页面,postman访问,得到json数据
通过postman,可以正常收到json数据
DRF做了个判断,是浏览器就回复页面,不是浏览器,回复json数据
在项目的settings.py配置文件中,有个配置项 REST_FRAMEWORK 专门用来配置DRF的
默认会有两个渲染器
看原码APIView里面的配置,renderer_classes指向DRF默认的配置
看下默认配置
如果觉得页面不好看,可以修改页面引入路径,自己写一个页面
如果自己在settings.py里面改DRF的配置将浏览器访问的渲染器给注释了,那么浏览器访问也会只返回json数据
只要自己写了REST_FRAMEWORK配置,就会把DRF的默认配置覆盖
用户级别的优先级大于系统默认配置的优先级
渲染器都是Response方法调用的,所以以后在DRF里面响应数据,不用JsonResponse了,直接用DRF的Response
Response方法的构造方式
def __init__(self, data=None, status=None, template_name=None, headers=None, exception=False, content_type=None): data数据不要是render处理过的数据,如Django的模型类对象。对于这样的数据我们可以使用序列化器 序列化处理后(转化为python字典类型数据)再传递给data参数 参数说明: data:为响应准备的序列化处理后的数据 status:状态码,默认200 return Response({'message':'ok'})
template_name: 模板名称,如果使用HTMLRenderer时需要指明,就是有些人觉得Response返回的那个页面不好看,就可以通过这个模板自行定制 前后端分离项目,也不需要展示响应页面
headers: 用于存放响应头信息的字典,比如存放一些cookie或者一些自定义的响应头都可以,例如:return Response ({‘msg’:‘ok’},status=204,headers={‘xx’:‘oo’})
content_type: 响应数据的Content-Type,通常此参数无需传递,REST_Framework会根据前端所需类型数据(accept请求头)来设置该参数
修改响应状态码
直接返回状态吗,前端人员可能有的状态码可能看不懂,DRF提供了带有英文单词的状态码
from rest_framework import status
原码定义了很多
视图函数中使用
获取浏览器url的查询参数,使用DRF的request.query_parmas
获得的是querydict类型的数据
多选传参,同一个参数,有多个值
打印出来的该键的值是包含多个值的列表
如果要获取该键的多个值,还是要使用getlist,如果使用get只能获取到列表的最后一个值
也可以使用request.GET,这样使用的就是View的老方法
之前老的request对象,用现在的request._request可以取到
GET请求获取单条记录,重新创建个视图类
获取单条数据走下面的路由
视图函数传参用来接收前端传来的查询参数
浏览器访问,查询到单条数据
路由那里也可以使用正则表达式的分组正则表达式来匹配路由
路由定义分组名
视图函数处通过分组名来接收前端url传过来的参数
当然路由也可以通过转换器类配置路由
2.POST请求,添加单条记录
#添加单条记录 def post(self,request): studentdata = request.data ser_obj = StudentSerializer(data=studentdata) if ser_obj.is_valid(): # obj = models.Student.objects.create(**ser_obj.validated_data) obj = ser_obj.save() new_obj = StudentSerializer(instance=obj) return Response(new_obj.data) else: print(ser_obj.errors) return Response({‘msg’:‘校验失败’},status=status.HTTP_400_BAD_REQUEST)
3.更新单条记录put方法
#更新单条记录 def put(self,request,query_id): studentdata = models.Student.objects.filter(id=query_id).first() #允许修改部分字段,不用全部都修改,使用partial=True,instance传的是模型类对象 ser_obj = StudentSerializer(instance= studentdata,data=request.data,partial=True) if ser_obj.is_valid(): obj = ser_obj.save() new_obj = StudentSerializer(instance=obj) return Response(new_obj.data) else: print(ser_obj.errors) return Response({‘msg’:‘校验失败’},status=status.HTTP_400_BAD_REQUEST)
修改id为2的数据
修改成功
4.DELETE请求
#删除单条数据 def delete(self, request,query_id): models.Student.objects.filter(id=query_id).delete() #删除成功响应一个空字符串,或者None return Response('',status=status.HTTP_204_NO_CONTENT)
把id为10的数据删掉,返回空字符串,达到预期
查看数据库表,可见id为10的数据已被删除
2) GenericAPIView[通用视图类]
继承自APIVIew,主要增加了操作序列化器和数据库查询的方法,作用是为下面Mixin扩展类的执行提供方法支持。通常在使用时,可搭配一个或多个Mixin扩展类。
提供的关于序列化器使用的属性与方法:
属性:
serializer_class 指明视图使用的序列化器。这种方式只能使用一个序列化器类
方法:
get_serializer_class(self)
当出现一个视图类中调用多个序列化器时,那么可以通过条件判断在get_serializer_class方法中通过返回不同的序列化器类名就可以让视图方法执行不同的序列化器对象了。
返回序列化器类,默认返回serializer_class,可以重写,例如:
#当试图中使用多个序列化器类时,可以使用该方法来区分,用户不同的请求方法,使用不同的序列化器 def get_serializer_class(self): print(‘111111111111’) if self.request.method == ‘GET’: return StudentModelSerializer2 else: return StudentModelSerializer get_serializer(self, *args, **kwargs)
返回序列化器对象,主要用来提供给Mixin扩展类使用,如果我们在视图中想要获取序列化器对象,也可以直接调用此方法。
注意,该方法在提供序列化器对象的时候,会向序列化器对象的context属性补充三个数据:request、format、view,这三个数据对象可以在定义序列化器时使用。
比如serializer = self.get_serializer(instance=self.get_object(),context={‘pk’:pk})`,下面的request和view我们后面会用到,现在先了解一下,后面使用就知道了
request 当前视图的请求对象
view 当前请求的类视图对象
format 当前请求期望返回的数据格式
提供的关于数据库查询的属性与方法:
属性:
queryset 指明使用的数据查询集
方法:
get_queryset(self)
返回视图使用的查询集,主要用来提供给Mixin扩展类使用,是列表视图与详情视图获取数据的基础,默认返回queryset属性,可以重写,例如:
def get_queryset(self):
user = self.request.user
return user.accounts.all()
get_object(self)
get_object()方法根据pk参数查找queryset中的数据对象
返回详情视图所需的模型类数据对象,主要用来提供给Mixin扩展类使用。
在试图中可以调用该方法获取详情信息的模型类对象。若详情访问的模型类对象不存在,会返回404。
该方法会默认使用APIView提供的check_object_permissions方法检查当前对象是否有权限被访问。
举例:
#url(r’^books/(?P\d+)/$', views.BookDetailView.as_view()), class BookDetailView(GenericAPIView): queryset = BookInfo.objects.all() serializer_class = BookInfoSerializer
def get(self, request, pk): book = self.get_object() # get_object()方法根据pk参数查找queryset中的数据对象 serializer = self.get_serializer(book) return Response(serializer.data)
通过self.get_object() 获取模型类对象,url中的传值参数必须是pk,否则报错
改为pk,就可以查到单条数据
原码看self.get_object(),看url路径信息
默认值:
lookup_field = ‘pk’
lookup_url_kwarg = None
所以url我们传参必须为pk
pk键值对,是在执行路由的as_view方法时,返回view方法,执行view方法中的setup方法时,封装进去的
self.kwargs获取的是字典,后赋属性,源码中不好找
如果路径中参数不想用pk,也可以自己修改,把lookup_field = ‘pk’ 拿过来,在视图类中,修改成自己定义的即可
但一般我们不用改
GenericAPIView五个接口,重新创建一个视图类继承GenericAPIView
url,如果使用get_object()方法 获取用户操作的数据,url处传参的参数必须是pk,因为 get_object()方法根据pk参数查找queryset中的数据对象
#基于GenericAPIView的视图接口,导包
from rest_framework.generics import GenericAPIView class Students2View(GenericAPIView): queryset = models.Student.objects.all() # 必须写这个参数 ,方法中使用的self.get_queryset()方法自动获取到queryset属性数据 serializer_class = StudentSerializer # 非必填属性,self.get_serializer获取到serializer_class制定的序列化器类。 通过这种方法只能使用一种序列化器类 # 当视图中使用多个序列化器类时,可以使用该方法get_serializer_class来区分 # def get_serializer_class(self): # print('111111111111') # if self.request.method == 'GET': # return StudentModelSerializer2 # else: # return StudentModelSerializer #获取所有数据 def get(self,request): #获取所有学生数据,queryset类型数据 studentdata = self.get_queryset() #通过self.get_serializer 可以动态获取序列化器 ser_obj = self.get_serializer(instance=studentdata,many=True) return Response(ser_obj.data) #添加单条数据 def post(self,request): data = request.data ser_obj = self.get_serializer(data=data) if ser_obj.is_valid(): obj = ser_obj.save() new_obj = self.get_serializer(instance=obj) return Response(new_obj.data,status=status.HTTP_201_CREATED) else: print(ser_obj.errors) return Response({'msg':'校验失败'},status=status.HTTP_400_BAD_REQUEST) #获取,修改删除单条数据 class Student2View(GenericAPIView): queryset = models.Student.objects.all() # 必须写这个参数 ,请求方法中使用的self.get_queryset()方法自动获取到queryset属性数据 serializer_class = StudentSerializer # 非必填属性,self.get_serializer获取到serializer_class制定的序列化器类 # 当试图中使用多个序列化器类时,可以使用该方法来区分 # def get_serializer_class(self): # print('111111111111') # if self.request.method == 'GET': # return StudentModelSerializer2 # else: # return StudentModelSerializer #获取单条数据,将pk传递进去 def get(self,request,pk): #get_object()方法根据pk参数查找queryset中的数据对象,参数必须用pk。否则报错 studentdata = self.get_object() ser_obj = self.get_serializer(instance=studentdata) return Response(ser_obj.data) #更新单条记录 def put(self,request,pk): studentdata = self.get_object() data = request.data ser_obj = self.get_serializer(instance=studentdata,data=data,partial=True) if ser_obj.is_valid(): # obj = models.Student.objects.create(**ser_obj.validated_data) obj = ser_obj.save() new_obj = self.get_serializer(instance=obj) return Response(new_obj.data,status=status.HTTP_201_CREATED) else: print(ser_obj.errors) return Response({'msg':'校验失败'},status=status.HTTP_400_BAD_REQUEST) #删除单条记录 def delete(self,request,pk): self.get_object().delete() return Response('',status=status.HTTP_204_NO_CONTENT)
新增或更新数据,url结尾一定要加/,不然Django会报错
删除不加/ 删除不掉数据
2.视图类中使用多个序列化器类的方法
当视图中使用多个序列化器类时,可以使用该方法get_serializer_class来区分
比如我们序列化器中有两个序列化器类
第二个序列化器类只序列化部分字段
现在我们两个序列化器都要用,使用serializer_class 指明序列化器类的方式已经实现不了了,此时就要用get_serializer_class 方法来控制,什么请求使用什么序列化器类
先把原来的serializer_class写死的序列化器注释掉
#当视图中使用多个序列化器类时,可以使用该方法来区分,方法名固定get_serializer_class def get_serializer_class(self): print(‘111111111111’) #get方法使用StudentSerializer2序列化器,其他请求使用StudentSerializer序列化器,通过self.request.method 获取请求的方法 if self.request.method == ‘GET’: return StudentSerializer2 else: return StudentSerializer
请求对象request也封装到了 self.request里面
GET请求:只拿到了name和age数据,符合要求
添加数据,可以正常校验,添加成功
打印一下请求时的序列化器类
get请求使用的是StudentSerializer2序列化器
3.基于视图扩展类的视图接口
使用了GenericAPIView视图类之后,我们发现代码并没有什么简化,反而还多了一个序列化器判断函数,不太好用。
生产中GenericAPIView并不是单独用的,而是要配合视图扩展类来用,才能体现GenericAPIView的强大之处
五个视图扩展类
五大扩展类接口:ListModelMixin,CreateModelMixin, RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin
作用:
提供了几种后端视图(对数据资源进行增删改查)处理流程的实现,如果需要编写的视图属于这五种,则视图可以通过继承相应的扩展类来复用代码,减少自己编写的代码量。
这五个扩展类需要搭配GenericAPIView父类,因为五个扩展类的实现需要调用GenericAPIView提供的序列化器与数据库查询的方法。
1)ListModelMixin
获取多条数据的 列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码。
该Mixin的list方法会对数据进行过滤和分页。
看原码:
2)CreateModelMixin
添加数据的创建视图扩展类,提供create(request, *args, **kwargs)
方法快速实现创建资源的视图,成功返回201状态码。
如果序列化器对前端发送的数据验证失败,返回400错误。
3)RetrieveModelMixin
获取单条数据,详情视图扩展类,提供retrieve(request, *args, **kwargs)
方法,可以快速实现返回一个存在的数据对象。
如果存在,返回200, 否则返回404。
4)UpdateModelMixin
更新视图扩展类,提供update(request, *args, **kwargs)方法,可以快速实现更新一个存在的数据对象。
同时也提供partial_update(request, *args, **kwargs)方法,可以实现局部更新。
成功返回200,序列化器校验数据失败时,返回400错误。
5)DestroyModelMixin
删除视图扩展类,提供destroy(request, *args, **kwargs)
方法,可以快速实现删除一个存在的数据对象。
成功返回204,不存在返回404。
导包:
from rest_framework.generics import GenericAPIView from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin #基于GenericAPIView的视图扩展类写接口 from rest_framework.generics import GenericAPIView from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin #注意,自己的视图类要继承GenericAPIView,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin class Students3View(GenericAPIView,ListModelMixin,CreateModelMixin): queryset = models.Student.objects.all() # 必须写这个参数 ,方法中使用的self.get_queryset()方法自动获取到queryset属性数据 serializer_class = StudentSerializer # 非必填属性,self.get_serializer获取到serializer_class制定的序列化器类 #获取所有数据 def get(self,request): return self.list(request) #添加单条数据 def post(self,request): return self.create(request) #获取,修改删除单条数据 class Student3View(GenericAPIView,RetrieveModelMixin, UpdateModelMixin,DestroyModelMixin): queryset = models.Student.objects.all() # 必须写这个参数 ,方法中使用的self.get_queryset()方法自动获取到queryset属性数据 serializer_class = StudentSerializer # 非必填属性,self.get_serializer获取到serializer_class制定的序列化器类 #获取单条数据 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)
4.基于GenericAPIView的视图子类的视图接口 子接口可以单独使用
from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView
1)CreateAPIView
提供 post 方法
继承自: GenericAPIView、CreateModelMixin
2)ListAPIView
提供 get 方法
继承自:GenericAPIView、ListModelMixin
3)RetrieveAPIView
提供 get 方法
继承自: GenericAPIView、RetrieveModelMixin
4)DestoryAPIView
提供 delete 方法
继承自:GenericAPIView、DestoryModelMixin
5)UpdateAPIView
提供 put 和 patch 方法
继承自:GenericAPIView、UpdateModelMixin
6)RetrieveUpdateAPIView
提供 get、put、patch方法
继承自: GenericAPIView、RetrieveModelMixin、UpdateModelMixin
7)RetrieveUpdateDestoryAPIView
提供 get、put、patch、delete方法
继承自:GenericAPIView、RetrieveModelMixin、UpdateModelMixin、DestoryModelMixin
看下原码,ListAPIView就是我们上面自己写的扩展接口,并且写了get方法
其他子类也都是帮我们写好了增删改查方法
我们需要写的:
#基于GenericAPIView的视图子类写接口 from rest_framework.generics import ListAPIView, CreateAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView #注意,自己的视图类要继承ListAPIView, CreateAPIView #增删改查,都帮你写完了,只需要写模型类,序列化器 class Students4View(ListAPIView, CreateAPIView): queryset = models.Student.objects.all() # 必须写这个参数 ,方法中使用的self.get_queryset()方法自动获取到queryset属性数据 serializer_class = StudentSerializer # 非必填属性,self.get_serializer获取到serializer_class制定的序列化器类 #获取,修改删除单条数据 class Student4View(RetrieveAPIView,UpdateAPIView,DestroyAPIView): queryset = models.Student.objects.all() # 必须写这个参数 ,方法中使用的self.get_queryset()方法自动获取到queryset属性数据 serializer_class = StudentSerializer # 非必填属性,self.get_serializer获取到serializer_class制定的序列化器类
路由:
postman GET请求:可以获取数据
增删改查请求都可以
当时用GenericAPIView扩展类 和子类视图的更新方法时,如果是更新部分字段,要使用patch方法,put方法更新,要将全部字段提交,否则不能修改
如下,put方法提供修改部分字段,不支持
使用patch方法可以:
如果想要put方法支持更新部分字段,针对于扩展类,可以在update方法中添加 partial=True
看原码,partial被剔除了,我们可以手动添加
5.视图集基类–ViewSet
视图集使用,必须写actions,即要在urls里面把相应请求方法绑定上,否则报错
1) 常用视图集父类
1. ViewSet
继承自APIView与ViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{‘get’:‘list’})的映射处理工作。
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
2.GenericViewSet
使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。
但是Mixin扩展类依赖于`GenericAPIView,所以还需要继承GenericAPIView。
GenericViewSe*就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
3.ModelViewSet
继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:
list() 提供一组数据
retrieve() 提供单个数据
create() 创建数据
update() 修改数据
destory() 删除数据
ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。
视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。如:
当我们使用了视图集基类,我们就可以自定义请求方法名了,不用使用固定的请求方法就是方法名了
而且可以把各种方法写到一个类中去
在设置路由时,在as_view方法中,以字典的形式将各个方法与对应的函数捆绑
#基于视图集基类ViewSet写接口,将所有请求,写在一个类中
from rest_framework.viewsets import ViewSet from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin from rest_framework.generics import GenericAPIView class Students5View(ViewSet,GenericAPIView,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): queryset = models.Student.objects.all() # 必须写这个参数 ,方法中使用的self.get_queryset()方法自动获取到queryset属性数据 serializer_class = StudentSerializer # 非必填属性,self.get_serializer获取到serializer_class制定的序列化器类 #获取所有数据 def get_all(self,request): return self.list(request) #获取单条数据 def get_one(self,request,pk): return self.retrieve(request,pk) #添加单条记录 def add_one(self,request): return self.create(request) #修改单条数据 def xiugai(self,request,pk): return self.partial_update(request,pk) #删除单条数据 def delete_one(self,request,pk): return self.destroy(request,pk)
路由:
简化写法:
#基于视图集基类ViewSet写接口 简化版 from rest_framework.viewsets import ViewSet from rest_framework.mixins import ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin from rest_framework.generics import GenericAPIView class Students6View(ViewSet,GenericAPIView,ListModelMixin,CreateModelMixin,RetrieveModelMixin,UpdateModelMixin,DestroyModelMixin): queryset = models.Student.objects.all() # 必须写这个参数 ,方法中使用的self.get_queryset()方法自动获取到queryset属性数据 serializer_class = StudentSerializer # 非必填属性,self.get_serializer获取到serializer_class制定的序列化器类
路由:
继续简化:引入模块太多了,继承的类也太多,需要简化
我们继承ModelViewSet,看原码,ModelViewSet已经继承了我们想要继承的类
#基于视图集基类ModelViewSet写接口 最简版 from rest_framework.viewsets import ModelViewSet class Students7View(ModelViewSet): queryset = models.Student.objects.all() # 必须写这个参数 ,方法中使用的self.get_queryset()方法自动获取到queryset属性数据 serializer_class = StudentSerializer # 非必填属性,self.get_serializer获取到serializer_class制定的序列化器类
视图集中定义附加action动作
在视图集中,除了上述默认的方法动作外,还可以添加自定义动作,进行扩展。
action属性
在视图集中,我们可以通过action对象属性来获取当前请求视图集时的action动作是哪个。
6.路由routers
对于视图集ViewSet,我们除了可以自己手动指明请求方式与动作action之间的对应关系外,还可以使用Routers来帮助我们快速实现路由信息。
REST framework提供了两个router
- SimpleRouter
- DefaultRouter
DefaultRouter:
使用方法:
在urls.py中配置
注册函数使用方法
register(prefix, viewset, base_name)
- prefix 该视图集的路由前缀
- viewset 视图集
- base_name 路由别名的前缀 #student-get student-login
浏览器随便访问一个页面:可以看到路由提示
正常访问
访问指定数据,可以修改删除
点击Api Root,可以查看统一的接口路径入口
添加路由数据:
可以有两种方式:
方式1 简单一些
urlpatterns = [ … ] urlpatterns += router.urls
方式2
urlpatterns = [ … url(r’^', include(router.urls)) ]
SimpleRouter:
使用方法与DefaultRouter相同
但是,SimpleRouter提供的路由少一些
而且没有Api Root
生产中不怎么使用Router功能,自动生成的路由比较少,想调整又不太方便。都是自己写,就算用,也用Default Router
基本不用Simple Router