视图组件

视图组件

cbv源码分析

1 views.Book.as_view()执行完,一定是个函数内存地址
2 as_view 是View类的类方法,类来调用
3 as_view中view这个闭包函数的源码是
	def view(request, *args, **kwargs):
		#核心代码,
		return self.dispatch(request, *args, **kwargs)
4 执行了View类的dispatch
    def dispatch(self, request, *args, **kwargs):
        # 如果是get请求
        # request.method.lower() 是get字符串,下面条件符合
        if request.method.lower() in self.http_method_names:
            #我们写的Book视图类的中国的get方法
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        # get(request, *args, **kwargs)---->执行Book视图类中的get方法,再往后就跟FBV一样了
        return handler(request, *args, **kwargs)
5 扩展,如果我在Book视图类中重写dispatch方法
	-可以实现,在get,post方法执行之前或者之后执行代码,完成类似装饰器的效果
       def dispatch(self, request, *args, **kwargs):
            # 加代码
            response=super().dispatch(request, *args, **kwargs)
            # 加代码
            return response

APIView的使用

1 APIView的使用

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request

class BookAPIView(APIView):
    def get(self, request, *args, **kwargs):
        # 这个request是什么?新的,drf的Reqeust对象,用起来,跟老request对象一样
        print(type(request))  #rest_framework.request.Request
        print(type(request._request)) #  django.core.handlers.wsgi.WSGIRequest
        print(request.GET)  # 神奇,
        print(request._request.GET)  # 应该这么用

        print(request.data)
        return Response('get请求')

    def post(self, request, *args, **kwargs):
        # 以后再取值,都从request.data中取
        print(request.data)
        print(request.POST)

        return Response('post请求')

2 APIView的执行流程(源码分析)

0 APIView继承了django的View
1 APIView中重写了as_view
    @classmethod
    def as_view(cls, **initkwargs):
        # 调用父类的as_view
        view = super().as_view(**initkwargs)
        # 只要继承APIView以后所有的视图都没有csrf保护了,不管是否注释掉中间件 
        return csrf_exempt(view)
2 再执行self.dispatch()---->APIView的dispatch(自己的没有去APIView中找)
    def dispatch(self, request, *args, **kwargs):
		# 包装了一个新的request对象,基于原来的request对象包装你的
        # 这个request对象是新的request对象,是drf提供的Request类的对象
        # 新的request包含,原来老的request(django的request)的内容
        # 在drf提供的Request类中:self._request = request
        request = self.initialize_request(request, *args, **kwargs)
        #print(request._request)  老的request对象
  
        try:
        	# 认证,权限,频率。。。
            self.initial(request, *args, **kwargs)
            if request.method.lower() in self.http_method_names:
                handler = getattr(self, request.method.lower(),
                                  self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            #视图类中get方法的执行
            response = handler(request, *args, **kwargs)
        except Exception as exc:
            # 如果在视图类中有错误,会被捕获(全局异常处理)
            response = self.handle_exception(exc)
        #把response包装了一下,返回了
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
	
    
3 在视图类中使用的request对象是新的request对象,老的是request._request
	-新的request.GET拿到的还是老的GET,原理如下(Request类重写了__getattr__)
       def __getattr__(self, attr):
            try:
                return getattr(self._request, attr)
            except AttributeError:
                return self.__getattribute__(attr)
            
4 结论:以后继承了APIView后,request对象成了新的,但是跟原来一样用

5 新的request对象中有一个属性  data
    - data是post请求携带的数据---->字典
    - 无论是什么编码格式,只要是post提交的数据,都在request.data中
    - 以后再取值,都从request.data中取

APIView和Request对象

1 以后如果使用了drf,继承APIView(drf提供了很多view,他们都是继承自APIView),执行流程如下
	-包装出了一个新的request,在视图函数中使用时,跟原来没有区别
    -注意:取post提交的数据,不要从request.POST中取了,要从request.data中取
    -注意:取get提交的数据,尽量不从request.GET中取了,要从request.query_params中取
    @property
    def query_params(self):
        return self._request.GET
    
2 Request类(drf的)中需要你掌握的
	-request.data  #方法包装成了数据属性
    -request.query_params #就是request._request.GET
    -request.FIELS        # 上传的文件
    -用起来跟原来一样
    
3 APIView类
	-包装新的request
    -执行了认证,权限,频率....
    -处理了全局异常
    -包装了response对象

GenericAPIView类

rest_framework.generics.GenericAPIView
GenericAPIView类是一个通用视图类,继承自APIView,主要增加了操作序列化器和数据库查询的方法
使用案例:多个五个接口APIView类公共部分取出后,不同的点在于它们的表和序列化类,而GenericAPIView类中可以定义一个表和序列化类的属性,后续CV修改接口时只需要修改这两个属性即可
    
# 案例: 写publish的5个接口(GenericAPIView--->继承了APIView)
from rest_framework.generics import GenericAPIView
from .models import Publish
from .serizlizer import PublishSerializer
class PublishView(GenericAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer
    def get(self,request):
        obj_list=self.get_queryset()
        ser=self.get_serializer(obj_list,many=True)
        return Response(ser.data)
    def post(self,request):
        ser = self.get_serializer(data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
class PublishDetailView(GenericAPIView):
    queryset = Publish.objects.all()
    serializer_class = PublishSerializer
    def get(self, request,pk):
        obj=self.get_object()
        ser = self.get_serializer(obj)
        return Response(ser.data)
    def delete(self,request,pk):
        obj=self.get_object()
        obj.delete()
        return Response()
    def put(self,request,pk):
        obj = self.get_object()
        ser=self.get_serializer(obj,data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)

两个视图基类总结

# APIView

# GenericAPIView
- queryset = None # 指明使用的数据库查询集,=表面.objects.all()
- serializer_class = None # 指明视图使用的序列化器, =序列化类名

- lookup_field = 'pk'     # 查单条时,无名,有名分组,转换器分出来的参数名叫pk 
- filter_backends         # 过滤类
- pagination_class        #分页类

- get_object(self) # 获取单个对象,查询单个时使用
- get_queryset(self) # 获取所有对象,查询所有时使用
- get_serializer(self, args, *kwargs) # 返回序列化类对象,可以传instance,data,many


五个视图扩展类

五个视图拓展类对应了五个接口的五个功能

用时先继承该类和GenericAPIView基类,写两个类属性,再在写对应的请求时调用类中的方法,

from rest_framework.mixins import 
CreateModelMixin	# 内部有create方法,就是新增
ListModelMixin		# 内部有list方法,就是查询所有
DestroyModelMixin	# 内部有destory方法,就是删除单条
UpdateModelMixin	# 内部有update方法,就是修改一条
RetrieveModelMixin	# 内部有retrieve方法,就是查询单条

class BookView(GenericAPIView,CreateModelMixin,ListModelMixin):
    queryset = Books.objects.all()
    serializer_class = BookSerializer
    def get(self,request):
        return self.list(request)
    def post(self,request):
        return self.create(request)

class BookDetailView(GenericAPIView,RetrieveModelMixin,DestroyModelMixin,UpdateModelMixin):
    queryset = Books.objects.all()
    serializer_class = BookSerializer

    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)

九个视图子类

在五个视图扩展类的基础上进行的功能的复写

在视图子类中,分别继承了对应的一个或多个视图拓展类和GenericAPIView基类,将对应的请求方式的方法进行的编写

我们只需要继承视图子类后写两个类属性,就能使用对应的请求功能,不需要再对请求方法重写

from rest_framework.generics import 
ListAPIView			# 查询所有
CreateAPIView		# 添加一个
ListCreateAPIView	# 查询所有、添加一个

UpdateAPIView		# 修改一个
DestroyAPIView		# 删除一个
RetrieveAPIView		# 查询一个
RetrieveUpdateAPIVie	# 查询一个、修改一个
RetrieveDestroyAPIView	# 查询一个、删除一个
RetrieveUpdateDestroyAPIView	# 查询一个、删除一个、修改一个

# 五个接口
class BookView(ListCreateAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializer
class BookDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializer

#  获取所有,删除1个
class BookView(ListAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializer
class BookDetailView(DestroyAPIView):
    queryset = Books.objects.all()
    serializer_class = BookSerializer

视图集

from rest_framework.viewsets import
ViewSetMixin  # 改变了路由写法,做了处理

ModelViewSet  # 5个接口
ReadOnlyModelViewSet  # 查询所有和查询一个的两接口

GenericViewSet  # 只是继承了ViewSetMixin, generics.GenericAPIView
ViewSet   # 只是继承了ViewSetMixin, views.APIView

# 如果路由想写成特殊的,且继承APIView---->直接继承ViewSet
# 如果路由想写成特殊的,且继承GenericAPIView---->直接继承GenericViewSet

以后自己写的方法只要做好映射,就可以通过指定的请求方式来触发自己的方法执行
def func1():
    print('from func1')
path('books/', views.BookView.as_view({'get':'func1'}))
# ViewSetMixin类 ---> 重写了as_view方法 ---> view和之前APIView的不一样了
for method, action in actions.items():
    handler = getattr(self, action)
	setattr(self, method, handler)
# 通反射将action字符串对应的方法能用method字符串调用,get()能调用list()方法;能够实现:请求方式和视图类中方法的映射
# 只要继承ViewSetMixin的视图类,以后路由写发特殊了
	-path('books/', views.BookView.as_view({'get':'list','post':'create'}))

drf视图组件继承关系图

posted @ 2021-07-23 20:52  zheng-sn  阅读(99)  评论(0)    收藏  举报