视图组件
视图组件
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视图组件继承关系图


浙公网安备 33010602011771号