基于django 开发的框架 jumpserver 源码解析(三)

小编 2026-06-24 阅读:1736 评论:0
基于 rest_framework 的 url 路由 跟 数据 跟 前端交互。   为了要让 django 支持 RESTful 风格 的 接口 需要 用到 第三方组件,来 分析下res...
    基于 rest_framework 的 url 路由 跟 数据 跟 前端交互。

      为了要让 django 支持 RESTful 风格 的 接口 需要 用到 第三方组件,来 分析下rest_framework  对 request 做了什么 ,又对 context 做了什么?

按照 惯例,先贴出源码
router = BulkRouter()
router.register(r\'v1/assets\', api.AssetViewSet, \'asset\')
router.register(r\'v1/admin-user\', api.AdminUserViewSet, \'admin-user\')
router.register(r\'v1/system-user\', api.SystemUserViewSet, \'system-user\')
router.register(r\'v1/labels\', api.LabelViewSet, \'label\')
router.register(r\'v1/nodes\', api.NodeViewSet, \'node\')
router.register(r\'v1/domain\', api.DomainViewSet, \'domain\')
router.register(r\'v1/gateway\', api.GatewayViewSet, \'gateway\')
jumpserver 大量使用了 这种形式 的 路由。但事实 what the fuck 基于类的视图为什么没有as_view() 方法? 其实as_view 方法 依然实现了,只不过又 封装了几层,更加抽象了一点。
这个 register 方法什么样,首先看源码。
class BaseRouter(six.with_metaclass(RenameRouterMethods)):
    def __init__(self):
        self.registry = []

    def register(self, prefix, viewset, basename=None, base_name=None):
        if base_name is not None:
            msg = \"The `base_name` argument is pending deprecation in favor of `basename`.\"
            warnings.warn(msg, PendingDeprecationWarning, 2)

        assert not (basename and base_name), (
            \"Do not provide both the `basename` and `base_name` arguments.\")

        if basename is None:
            basename = base_name

        if basename is None:
            basename = self.get_default_basename(viewset)
        self.registry.append((prefix, viewset, basename))
其实register 方法 是调用了父类BaseRouter 的 register ,这个 方法 其实就是在内部 维持了一个 列表registry,这个 列表放了 prefix,viewset ,basename。接着往下看,
urlpatterns += router.urls

 源码中用到了 router的urls 属性。

接下来 看 urls 属性应该怎么看?

 @property
    def urls(self):
        if not hasattr(self, \'_urls\'):
            self._urls = self.get_urls()
        return self._urls

这个 urls 属性调用了 get_urls 方法。而 get_urls 方法 实在父类SimpleRouter中。

 def get_urls(self):
        \"\"\"
        Use the registered viewsets to generate a list of URL patterns.
        \"\"\"
        ret = []

        for prefix, viewset, basename in self.registry:
            lookup = self.get_lookup_regex(viewset)
            routes = self.get_routes(viewset)

            for route in routes:

                # Only actions which actually exist on the viewset will be bound
                mapping = self.get_method_map(viewset, route.mapping)
                if not mapping:
                    continue

                # Build the url pattern
                regex = route.url.format(
                    prefix=prefix,
                    lookup=lookup,
                    trailing_slash=self.trailing_slash
                )

                # If there is no prefix, the first part of the url is probably
                #   controlled by project\'s urls.py and the router is in an app,
                #   so a slash in the beginning will (A) cause Django to give
                #   warnings and (B) generate URLS that will require using \'//\'.
                if not prefix and regex[:2] == \'^/\':
                    regex = \'^\' + regex[2:]

                initkwargs = route.initkwargs.copy()
                initkwargs.update({
                    \'basename\': basename,
                    \'detail\': route.detail,
                })

                view = viewset.as_view(mapping, **initkwargs)
                name = route.name.format(basename=basename)
                ret.append(url(regex, view, name=name))

        return ret

这个 get_url 方法 就是 取出刚才 在 regester 方法中放进registry列表中 中的 东西,然后遍历 routes 列表,route列表存放了Route容器,源码如下。

outes = [
        # List route.
        Route(
            url=r\'^{prefix}{trailing_slash}$\',
            mapping={
                \'get\': \'list\',
                \'post\': \'create\'
            },
            name=\'{basename}-list\',
            detail=False,
            initkwargs={\'suffix\': \'List\'}
        ),
        # Dynamically generated list routes. Generated using
        # @action(detail=False) decorator on methods of the viewset.
        DynamicRoute(
            url=r\'^{prefix}/{url_path}{trailing_slash}$\',
            name=\'{basename}-{url_name}\',
            detail=False,
            initkwargs={}
        ),
        # Detail route.
        Route(
            url=r\'^{prefix}/{lookup}{trailing_slash}$\',
            mapping={
                \'get\': \'retrieve\',
                \'put\': \'update\',
                \'patch\': \'partial_update\',
                \'delete\': \'destroy\'
            },
            name=\'{basename}-detail\',
            detail=True,
            initkwargs={\'suffix\': \'Instance\'}
        ),
        # Dynamically generated detail routes. Generated using
        # @action(detail=True) decorator on methods of the viewset.
        DynamicRoute(
            url=r\'^{prefix}/{lookup}/{url_path}{trailing_slash}$\',
            name=\'{basename}-{url_name}\',
            detail=True,
            initkwargs={}
        ),
    ]

遍历route 列表,调用 get_method_map 这个方法,这个方法源码如下。

 def get_method_map(self, viewset, method_map):
        \"\"\"
        Given a viewset, and a mapping of http methods to actions,
        return a new mapping which only includes any mappings that
        are actually implemented by the viewset.
        \"\"\"
        bound_methods = {}
        for method, action in method_map.items():
            if hasattr(viewset, action):
                bound_methods[method] = action
        return bound_methods

其实这个方法 就是  返回了一个 method 跟 action 的 映射,mapping。然后把这个mapping 作为参数 放进 视图类的 as_view 方法中。那 基于 rest_framework 的 as_view 跟普通  类视图 有什么不一样?

举个例子,jumpserver 中 常用的BulkModelViewSet 类,当中的as_view 方法,需要在父类中找,按照python 最新版,多继承的寻址顺序为 从左 至右,广度优先的原则。比如

class A (object):
    pass

class B(A):
    pass

class C(A):
    pass


class E(B,C):
    pass


if __name__ == \'__main__\':
    print(E.__mro__)

 

就是 多继承 寻找方法顺序为 E >> B>>C>>A>>object.

class BulkModelViewSet(bulk_mixins.BulkCreateModelMixin,
                       bulk_mixins.BulkUpdateModelMixin,
                       bulk_mixins.BulkDestroyModelMixin,
                       ModelViewSet):
    pass

 

最后在BulkModelViewSet 的 父类 ModelViewSet 的父类  GenericViewSet 的父类 ViewSetMixin 找到了 as_view 方法。

源码贴出 如下,这个方法 ,actions 就是刚才传进去的mapping 参数,然后dispaher 方法,ViewSetMixin 本身没有实现,老办法找 父类,发现,其实父类 使用得是 ajdango 得  base 类View dispath 方法,这里就不贴出了,然后调用视图类 经行映射得那个方法比如 get——list ,或者 post-create 方法。


    @classonlymethod
    def as_view(cls, actions=None, **initkwargs):
        \"\"\"
        Because of the way class based views create a closure around the
        instantiated view, we need to totally reimplement `.as_view`,
        and slightly modify the view function that is created and returned.
        \"\"\"
        # The name and description initkwargs may be explicitly overridden for
        # certain route confiugurations. eg, names of extra actions.
        cls.name = None
        cls.description = None

        # The suffix initkwarg is reserved for displaying the viewset type.
        # This initkwarg should have no effect if the name is provided.
        # eg. \'List\' or \'Instance\'.
        cls.suffix = None

        # The detail initkwarg is reserved for introspecting the viewset type.
        cls.detail = None

        # Setting a basename allows a view to reverse its action urls. This
        # value is provided by the router through the initkwargs.
        cls.basename = None

        # actions must not be empty
        if not actions:
            raise TypeError(\"The `actions` argument must be provided when \"
                            \"calling `.as_view()` on a ViewSet. For example \"
                            \"`.as_view({\'get\': \'list\'})`\")

        # sanitize keyword arguments
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError(\"You tried to pass in the %s method name as a \"
                                \"keyword argument to %s(). Don\'t do that.\"
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError(\"%s() received an invalid keyword %r\" % (
                    cls.__name__, key))

        # name and suffix are mutually exclusive
        if \'name\' in initkwargs and \'suffix\' in initkwargs:
            raise TypeError(\"%s() received both `name` and `suffix`, which are \"
                            \"mutually exclusive arguments.\" % (cls.__name__))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            # We also store the mapping of request methods to actions,
            # so that we can later set the action attribute.
            # eg. `self.action = \'list\'` on an incoming GET request.
            self.action_map = actions

            # Bind methods to actions
            # This is the bit that\'s different to a standard view
            for method, action in actions.items():
                handler = getattr(self, action)
                setattr(self, method, handler)

            if hasattr(self, \'get\') and not hasattr(self, \'head\'):
                self.head = self.get

            self.request = request
            self.args = args
            self.kwargs = kwargs

            # And continue as usual
            return self.dispatch(request, *args, **kwargs)

        # 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=())

        # We need to set these on the view function, so that breadcrumb
        # generation can pick out these bits of information from a
        # resolved URL.
        view.cls = cls
        view.initkwargs = initkwargs
        view.actions = actions
        return csrf_exempt(view)

 

 

 

 

 

 

 

 

 

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表