Python 学习笔记(十四)--Django REST Framework中ViewSets 与APIView
一. ViewSets
1.1 常见导入使用
from rest_framework.viewsets import ModelViewSet
1.2 功能描述
""" ViewSets are essentially just a type of class based view, that doesn't provide any method handlers, such as `get()`, `post()`, etc... but instead has actions, such as `list()`, `retrieve()`, `create()`, etc... Actions are only bound to methods at the point of instantiating the views. user_list = UserViewSet.as_view({'get': 'list'}) user_detail = UserViewSet.as_view({'get': 'retrieve'}) Typically, rather than instantiate views from viewsets directly, you'll register the viewset with a router and let the URL conf be determined automatically. router = DefaultRouter() router.register(r'users', UserViewSet, 'user') urlpatterns = router.urls """
1.3 类的定义(继承关系)
class ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, mixins.ListModelMixin, GenericViewSet):
1.4 继承类 GenericViewSet的定义
class GenericViewSet(ViewSetMixin, generics.GenericAPIView): """ The GenericViewSet class does not provide any actions by default, but does include the base set of generic view behavior, such as the `get_object` and `get_queryset` methods. """ pass
这个来自导入
from rest_framework import generics, views
二 进入generics
继续看 generics 中的GenericAPIView
class GenericAPIView(views.APIView): """ Base class for all other generic views. """ # You'll need to either set these attributes, # or override `get_queryset()`/`get_serializer_class()`. # If you are overriding a view method, it is important that you call # `get_queryset()` instead of accessing the `queryset` property directly, # as `queryset` will get evaluated only once, and those results are cached # for all subsequent requests.
注意继承关系,来自于
from rest_framework import views
三 继续views【rest_framework中的views】
APIView是REST framework提供的所有视图的基类,继承自Django的View父类。
""" Provides an APIView class that is the base of all views in REST framework. """
class APIView(View): # The following policies may be set at either globally, or per-view.
View的继承就是django中原生的view了。
from django.views.generic import View
我们看下APIView中的主要方法as_view()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @classmethod def as_view( cls , * * initkwargs): """ Store the original class on the view function. This allows us to discover information about the view when we do URL reverse lookups. Used for breadcrumb generation. """ if isinstance ( getattr ( cls , 'queryset' , None ), models.query.QuerySet): def force_evaluation(): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, ' 'as the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls .queryset._fetch_all = force_evaluation view = super ().as_view( * * initkwargs) #####这一句是核心代码;关键代码 view. cls = cls view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. return csrf_exempt(view) |
拨茧抽丝,主要内容是调用父类的as_view()。
调用的dispatch的伪代码如下,实际真实的代码请参照源码。
def dispatch(self, request, *args, **kwargs): # 构建新的request对象 request = self.initialize_request(request, *args, **kwargs) self.request = request self.initial(request, *args, **kwargs) ## 上面一行的代码主要就是初始化:认证、权限、限流。 # Get the appropriate handler method--进行调度分发 if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) response = handler(request, *args, **kwargs) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
四.django中原生的view
源码位置;
主要方法
@classonlymethod ##指明是类方法,通过类可以直接调用。会把类自动传入。 def as_view(cls, **initkwargs): ##把类cls自动传入;intikwargs 还可以接受其他参数.
##为了更好的理解cls,补充下,谁调用类方法as_view,cls就是谁;
##即 xxx.as_viw(),cls 就是类xxx。
##例如 path("wflow/log/", wflow.WflowLog.as_view()),cls就是类WflowLog。 """Main entry point for a request-response process.""" for key in initkwargs: if key in cls.http_method_names: raise TypeError( 'The method name %s is not accepted as a keyword argument ' 'to %s().' % (key, cls.__name__) ) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): ###request为当此请求的request对象。 self = cls(**initkwargs) ###实例化得到一个对象。对象---你自己之前定义的model,也就是传入的cls类型,urls中配置的类。 self.setup(request, *args, **kwargs) if not hasattr(self, 'request'): raise AttributeError( "%s instance has no 'request' attribute. Did you override " "setup() and forget to call super()?" % cls.__name__ ) return self.dispatch(request, *args, **kwargs) ##request为当次请求的request。这个return返回是as_view ,
##是view 方法的真正返回。进一步思考分析,dispatch()去那里找,或者说是谁的方法?首先
##去cls这个类中去找,即urls中配置的那个类,没有的话,再去父类,也就是
##django.views.generic中的View的dispatch()方法。 view.view_class = cls view.view_initkwargs = initkwargs # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) return view ###注意,返回的是一个叫view的内层函数(或者说是内层函数的内存地址)。请求一旦来了,如果url匹配成功,会把当次请求的request对象,
###作为一个参数,传送给这个内层函数,来执行。
其中调用到的基础方法
def setup(self, request, *args, **kwargs): """Initialize attributes shared by all view methods.""" if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs
在看dispatch()之前,先补充下getattr()函数的基本知识。getattr()函数是Python中的一个内置函数,用于获取对象的指定属性值。
getattr(object, name[, default]) * object -- 对象。 * name -- 字符串,对象属性。 * default -- 默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError。
通过使用getattr()函数,可以实现以下功能(1)动态调用对象方法;(2)访问对象属性列表;(3)处理配置文件;(4)处理动态属性名称等。
def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list.
##request为当次请求的request
## 判断请求方式在不在允许的请求方式里 if request.method.lower() in self.http_method_names:
##handler映射为你定义的model中的那个方法(或叫作内存地址),例如自己定义的类中的get()方法。 handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
## 换言之,handler赋值后就是self.get 或 self.post 等中的一种。
## 再明确一般,上面的self是urls中配置的那个类的实例化对象,也就是上面的self = cls(**initkwargs)生成的对象。 else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) ###执行定义的方法。什么方法呢?是self = cls(**initkwargs)实例化生成的对象中的
### get、post、put、delete中某一种方法。当然,具体是哪一种,要看请求对象的
### request.method.lower()。
总结,使用getattr()函数,进行动态方法调用。换言之,经过dispatch()指派作用,将request.method.lower()的方法和定义的类中的方法进行的绑定或者说任务的指派。
五. APIView中的源码分析
5.1 一般导入
from rest_framework.views import APIView
5.2 主要的方法可看本文第三部分的截图
5.3 方法as_view()
@classmethod def as_view(cls, **initkwargs): ##同样也是类的绑定方法 """ Store the original class on the view function. This allows us to discover information about the view when we do URL reverse lookups. Used for breadcrumb generation. """ if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): def force_evaluation(): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, ' 'as the result will be cached and reused between requests. ' 'Use `.all()` or call `.get_queryset()` instead.' ) cls.queryset._fetch_all = force_evaluation view = super().as_view(**initkwargs) ##调用父类(django的view)的as_view方法; view.cls = cls ##注意,python中一切皆对象;函数也是对象。是对象,就可以有属性,有值。 view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. ##以后所有的请求,都没有csrf认证了。【只要继承了APIVeiw,就没有crsf的认证了,而是运用了自己的自己的认证】 return csrf_exempt(view) ##局部禁用csrf --- 还有已用用法:就是在视图函数上加装饰器@crsf_exempt
需要特别留意的是view中调用的dispatch()是APIView中的dispatch()方法。
【首先用户定义的类中去找-->找不到,再在APIView中去找】---说明: 请求来了-->匹配url-->view(request)--->self.dispatch() [注意self是APIView对象]
# Note: Views are made CSRF exempt from within `as_view` as to prevent # accidental removal of this exemption in cases where `dispatch` needs to # be overridden. def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs
###请求模块(解析模块) request = self.initialize_request(request, *args, **kwargs) ##参数中的request是当次请求的request对象;返回的是一个新的request对象,
##不在是django原生的request了,
##是drf自己定义的request。 self.request = request ##赋值的是一个新的request对象 self.headers = self.default_response_headers # deprecate? try:
###三大认证模块 self.initial(request, *args, **kwargs) # Get the appropriate handler method 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 ###响应模块 response = handler(request, *args, **kwargs) ###异常模块 except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
请求模块(解析模块)调用的方法
def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """
##准备要解析的数据
parser_context = self.get_parser_context(request) ##重新打包 return Request( request,
##获取解析类 parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context )
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
2018-12-24 关于SQL Server 数据库归档的一些思考和改进