day06 drf视图组件
今日内容
1、两个视图基类
1.1 APIView
from rest_framework.views import APIView
1.11 APIView的属性
APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
APIView与View的不同之处在于:
1.传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象;
视图方法可以返回REST framework的Response对象,视图会为响应数据设置(render)符合前端要求的格式;
2.任何APIException异常都会被捕获到,并且处理成合适的响应信息;
3.在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制。
# 支持定义的类属性
1.authentication_classes 列表或元祖,身份认证类
2.permissoin_classes 列表或元祖,权限检查类
3.throttle_classes 列表或元祖,流量控制类
# 在APIView中仍以常规的类视图定义方法来实现get() 、post() 或者其他请求方式的方法。
1.2 GenericAPIView[通用视图类]
from rest_framework.generics import GenericAPIView
1.2.1 类属性和三个方法
# 类属性
queryset = None # 所有数据
serializer_class = None # 序列化的类
lookup_field = 'pk' # 查询单条转换器的字段
queryset = Publish.objects.all() # 只要配置这两行
serializer_class = PublishModelSerializer
# 三个方法
self.get_queryset() # 获取所有数据,指明使用的数据查询集
self.get_serializer # 获取序列化类,指明视图使用的序列化器
self.get_object() # 获取单条
1.2.1 基本使用
# 通过继承GenericAPIView 写5个接口
from rest_framework.generics import GenericAPIView # 继承APIView,内部写了几个方法
class PublishView(GenericAPIView):
queryset = Publish.objects.all() # 只要配置这两行
serializer_class = PublishModelSerializer
def get(self, request, *args, **kwargs):
# 查询所有的图书
obj = self.get_queryset() # 内部做了questset.all()
ser = self.get_serializer(instance=obj, many=True)
return Response(data=ser.data)
# 新增接口
def post(self, request, *args, **kwargs):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
class PublishDetailView(GenericAPIView):
queryset = Publish.objects.all() # 只要配置这两行
serializer_class = PublishModelSerializer
# 查询单本图书
def get(self, request, pk):
# lookup_field = 'pk' 根据pk参数,类中定义好了
obj = self.get_object()
ser = self.get_serializer(instance=obj)
return Response(ser.data)
# 删除接口
def delete(self, request, pk):
res = self.get_object().delete() # 因为返回的是一个元祖,取第一个值
if res[0] >= 1: # 影响了几行
return Response()
else:
return Response('要删除的数据不存在')
# 修改接口
def put(self, request, pk):
obj = self.get_object()
ser = self.get_serializer(instance=obj, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
'''
总结:
1.先要继承GenericAPIView类
2.配置这两个参数:queryset、serializer_class
3.书写类中方法
'''
2、5个视图扩展类
2.1 基本介绍
from rest_framework.mixins import ListModelMixin,CreateModelMixin,DestroyModelMixin,UpdateModelMixin,RetrieveModelMixin
# 导入在这里面导入
ListModelMixin: 获取所有数据的视图扩展类 # list()
CreateModelMixin: 新增数据的视图扩展类 # create()
DestroyModelMixin: 删除数据的视图扩展类 # destroy()
UpdateModelMixin: 修改数据的视图扩展类 # update()
RetrieveModelMixin: 获取单条数据的视图扩展类 # retrieve()
# 5个视图扩展类,不是视图类,必须配合视图类才能使用
2.2 仿源码写了5个视图扩展类
2.21 my_minins.py
# 封装5个方法
from rest_framework.response import Response
# 获取所有数据的视图扩展类
class ListModelMixin:
def list(self, request, *args, **kwargs):
book_list = self.get_queryset()
ser = self.get_serializer(instance=book_list, many=True)
return Response(ser.data)
# 新增数据的视图扩展类
class CreateModelMixin:
def create(self, request, *args, **kwargs):
ser = self.get_serializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
# 删除数据的视图扩展类
class DestroyModelMixin:
def destroy(self, request, pk):
res = self.get_object().delete()
if res[0] >= 1:
return Response()
else:
return Response('要删除的数据不存在')
# 修改数据的视图扩展类
class UpdateModelMixin:
def update(self, request, pk):
book_list = self.get_object()
ser = self.get_serializer(instance=book_list, data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data)
else:
return Response(ser.errors)
# 获取单条数据的视图扩展类
class RetrieveModelMixin:
def retrieve(self, request, pk):
book_list = self.get_object()
ser = self.get_serializer(instance=book_list)
return Response(ser.data)
'''根据之前有封装了一层方法,是为第三层,还能在简写代码'''
2.22 views.py
# 通过GenericAPIView+5个视图扩展类来实现5个接口
from rest_framework.response import Response
from app01.models import Book, Publish, Author, AuthorDetail
from app01.serializer import BookModelSerializer
from rest_framework.generics import GenericAPIView
from app01.my_mixins import ListModelMixin,CreateModelMixin,DestroyModelMixin,UpdateModelMixin,RetrieveModelMixin
# 用哪个功能就继承相应的类
class BookViews(GenericAPIView, ListModelMixin, CreateModelMixin):
queryset = Book.objects.all()
serializer_class = BookModelSerializer
# 查询所有图书接口
def get(self, request, *args, **kwargs):
return self.list(request)
def post(self, request, *args, **kwargs):
return self.create(request)
# 用哪个功能就继承相应的类
class BookDetailViews(GenericAPIView, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin):
queryset = Book.objects.all()
serializer_class = BookModelSerializer
def get(self, request, pk):
return self.retrieve(request, pk)
def delete(self, request, pk):
return self.destroy(request, pk)
def put(self, request, pk):
return self.update(request, pk)
'''
总结:
1.使用这5个方法类的前提是:必须继承GenericAPIView类
1.把5个功能封装成5个扩展类
2.通过继承使用该方法
'''
3、9个视图子类
3.1 基本介绍
from rest_framework.generics import ListAPIView, CreateAPIView, ListCreateAPIView, UpdateAPIView, DestroyAPIView, RetrieveAPIView
from rest_framework.generics import RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView
'''
原理:
其实把一个个接口封装个一个类,视图函数使用哪个功能就继承哪个类即可
'''
# 视图子类详解:
1.ListAPIView: 获取所有数据接口
2.CreateAPIView: 新增数据接口
3.ListCreateAPIView: 获取所有数据接口和新增数据接口
4.UpdateAPIView: 修改数据接口
5.DestroyAPIView: 删除数据接口
6.RetrieveAPIView: 获取单条数据接口
7.RetrieveUpdateDestroyAPIView: 获取单条数据、删除、修改接口
8.RetrieveDestroyAPIView: 获取单条数据和删除接口
9.RetrieveUpdateAPIView: 获取单条数据和修改接口
3.2 仿写源码写9个视图子类
3.2.1 my_generics.py
'''5个视图扩展类+9个视图子类才算是完整的功能'''
from app01.my_mixins import ListModelMixin, CreateModelMixin, DestroyModelMixin, UpdateModelMixin, RetrieveModelMixin
from rest_framework.generics import GenericAPIView
from app01.my_generics import RetrieveUpdateDestroyAPIView, RetrieveDestroyAPIView, RetrieveUpdateAPIView
class ListAPIView(GenericAPIView, ListModelMixin):
# 查询所有图书接口
def get(self, request, *args, **kwargs):
return self.list(request)
class CreateAPIView(GenericAPIView, CreateModelMixin):
# 新增数据接口
def post(self, request, *args, **kwargs):
return self.create(request)
class RetrieveAPIView(GenericAPIView, RetrieveModelMixin):
# 获取单条数据接口
def get(self, request, pk):
return self.retrieve(request, pk)
class DestroyAPIView(GenericAPIView, DestroyModelMixin):
# 删除接口
def delete(self, request, pk):
return self.destroy(request, pk)
class UpdateAPIView(GenericAPIView, UpdateModelMixin):
# 修改数据接口
def put(self, request, pk):
return self.update(request, pk)
# 获取所有和新增
class ListCreateAPIView(GenericAPIView, ListModelMixin, CreateModelMixin):
def get(self, request, *args, **kwargs):
return self.list(request)
def post(self, request, *args, **kwargs):
return self.create(request)
# 获取单条数据、删除、修改接口
class RetrieveUpdateDestroyAPIView(GenericAPIView, RetrieveModelMixin, DestroyModelMixin, UpdateModelMixin):
# 获取单条数据接口
def get(self, request, pk):
return self.retrieve(request, pk)
# 删除接口
def delete(self, request, pk):
return self.destroy(request, pk)
# 修改数据接口
def put(self, request, pk):
return self.update(request, pk)
# 获取单条数据和删除接口
class RetrieveDestroyAPIView(GenericAPIView, RetrieveModelMixin, DestroyModelMixin):
# 获取单条数据接口
def get(self, request, pk):
return self.retrieve(request, pk)
# 删除接口
def delete(self, request, pk):
return self.destroy(request, pk)
# 获取单条数据和修改接口
class RetrieveUpdateAPIView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin):
# 获取单条数据接口
def get(self, request, pk):
return self.retrieve(request, pk)
# 修改数据接口
def put(self, request, pk):
return self.update(request, pk)
'''根据之前有封装了一层接口,是为第四层,还能在简写代码'''
3.2.2 views.py
from app01.my_generics import ListAPIView, CreateAPIView, ListCreateAPIView, UpdateAPIView, DestroyAPIView, RetrieveAPIView
class BookViews(ListCreateAPIView): # 想使用什么功能继承什么类
queryset = Book.objects.all()
serializer_class = BookModelSerializer
class BookDetailViews(RetrieveUpdateDestroyAPIView):
queryset = Book.objects.all()
serializer_class = BookModelSerializer
'''
总结:
1.把5个接口封装成9个功能结合类,通过继承视图子类实现该接口功能
2.使用什么功能,继承该接口视图子类
'''
4、视图集
4.1 常用视图集父类
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet, ViewSetMixin, GenericViewSet
4.1.1 ViewSet
# 继承自APIView与ViewSetMixin,作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。
ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{‘get’:’list’})的映射处理工作。
在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。
class LoginView(ViewSet): # 继承ViewSet和继承下面的是一个效果
class LoginView(ViewSetMixin, APIView): # ViewSetMixin只是重写了as_view,但是没有继承View,所以还有继承APIView才是视图基类
4.1.2 GenericViewSet
使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。
# GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。
class LoginView(GenericViewSet): # 其实就是同时继承了下面两个
class LoginView(ViewSetMixin, GenericAPIView):
4.1.3 ViewSetMixin
# 视图层
from rest_framework.viewsets import ViewSetMixin,ViewSet
'''同时继承两个类太不方便了,'''
# class LoginView(ViewSet): 继承ViewSet和继承下面的是一个效果
class LoginView(ViewSetMixin, APIView): # ViewSetMixin只是重写了as_view,但是没有继承View,所以还有继承APIView才是视图基类
def login(self,request):
return Response('登录成功')
# 路由层
from app01 import views
urlpatterns = [
path('login/', views.LoginView.as_view({'post': 'login'})), # 请求去视图函数里面,是哪个请求执行对应方法
]
'''
总结:
1.ViewSetMixin必须和视图基类同时被继承
2.ViewSetMixin中可以书写别的方法
3.以后视图类中可以写任意的方法,只需要继承ViewSetMixin后,使用路由做映射即可
'''
4.1.4 ModlViewSet
# 继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。
例子:
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet, ViewSetMixin, GenericViewSet
# 继承这个5个接口都有了,但是5个接口有两个get
class PublishViews(ModelViewSet):
queryset = Publish.objects.all()
serializer_class = PublishModelSerializer
# 必须去路由层从新配置({'get':'list'})的映射处理工作
from app01 import views
urlpatterns = [
path('publish/', views.PublishViews.as_view({'get': 'list', 'post': 'create'})), # 请求去视图函数里面,是哪个请求执行对应方法
path('publish/', views.PublishViews.as_view({'get': 'retrieve', 'delete': 'destroy', 'put': 'update'})),
]
'''
总结:
1.继承ModelViewSet,然后在修改路由,就可以实现5个接口
2.但是里面必须重写许多方法,比较麻烦
3.为什么要这样书写路由?
因为ModelViewSet里面有一个GenericViewSet类里面,
重新书写了as_view()方法,所以这里执行的是GenericViewSet里面的as_view()方法
'''
4.1.5 ReadOnlyModelViewSet
# ReadOnlyModelViewSet:只读,只能获取所有和单条数据,其余操作不可以。
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet, ViewSet, ViewSetMixin, GenericViewSet
class PublishView(ReadOnlyModelViewSet):
queryset = Publish.objects.all()
serializer_class = PublishSerializer
from app01 import views
urlpatterns = [
path('publish/', views.PublishViews.as_view({'get': 'list'})), # 请求去视图函数里面,是哪个请求执行对应方法
path('publish/', views.PublishViews.as_view({'get': 'retrieve'})),
]
5、ModelViewSet源码解析
from rest_framework.viewsets import ModelViewSet # 入口
1.进入ModelViewSet里面
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet): # 进入这里面
# 因为ModelViewSet视图集中里面有mixins的5个视图扩展类
2. 进入GenericViewSet里面
class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
# ViewSetMixin重要的在里面
'''
ViewSetMixin在最左面,优先级最高,ViewSetMixin也继承了View
优先执行ViewSetMixin中as_view方法
'''
3.进入ViewSetMixin里面
# 路由之所以可以这么书写:views.PublishViews.as_view({'get': 'list'}),是因为ViewSetMixin里面重新写了as_view方法
class ViewSetMixin:
@classonlymethod
def as_view(cls, actions=None, **initkwargs): # actions就是{'get': 'list'}
"""
This is the magic.
view = MyViewSet.as_view({'get': 'list', 'post': 'create'})
"""
# 如果没有传,直接抛异常
if not actions:
raise TypeError("The `actions` argument must be provided when "
"calling `.as_view()` on a ViewSet. For example "
"`.as_view({'get': 'list'})`")
# views.PublishViews.as_view这里是ViewSetMixin里面的view内存地址,加括号调用
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# 其中mothod:get(请求),action:list(方法)
for method, action in actions.items():
# 利用反射机制去视图类调用action方法
handler = getattr(self, action) # handler:get..
# 再利用反射机制映射方法:{'get':'list '}
setattr(self, method, handler)
self.request = request
self.args = args
self.kwargs = kwargs
# 调用dispatch方法,按照顺序找这个方法
return self.dispatch(request, *args, **kwargs)
return csrf_exempt(view)
镇楼图