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 )
复制代码

 

posted @   东山絮柳仔  阅读(246)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
历史上的今天:
2018-12-24 关于SQL Server 数据库归档的一些思考和改进
点击右上角即可分享
微信分享提示