django中的proxy

在看django源码的时候, django.db.__init__.py里面有这样一行代码

connections = ConnectionHandler()
connection = ConnectionProxy(connections, DEFAULT_DB_ALIAS)

从其他各处都能看到各种导入这个connection, 然后就能直接连数据库了。

而点进去ConnectionProxy中去看, 发现了如下的代码:

class ConnectionProxy:
    """Proxy for accessing a connection object's attributes."""

    def __init__(self, connections, alias):
        self.__dict__["_connections"] = connections
        self.__dict__["_alias"] = alias

    def __getattr__(self, item):
        return getattr(self._connections[self._alias], item)

    def __setattr__(self, name, value):
        return setattr(self._connections[self._alias], name, value)

    def __delattr__(self, name):
        return delattr(self._connections[self._alias], name)

    def __contains__(self, key):
        return key in self._connections[self._alias]

    def __eq__(self, other):
        return self._connections[self._alias] == other

好像拿到这个东西, 也没办法直接连数据库, 那django是怎么连接数据库的呢?

首先, 上面传入的connections, 是一个Connectionhandler, 而在Connectionhandler的父类BaseConnectionHandler中, 有针对属性做了特殊处理

    def __getitem__(self, alias):
        try:
            return getattr(self._connections, alias)
        except AttributeError:
            if alias not in self.settings:
                raise self.exception_class(f"The connection '{alias}' doesn't exist.")
        conn = self.create_connection(alias)
        setattr(self._connections, alias, conn)
        return conn

也就是获取一个alias例如default的连接的时候, 如果之前没有建立起这个连接, 就会根据配置, 从django.db.backend中依据配置中的引擎去加载对应的连接类, 然后建立起来连接实例。

    def create_connection(self, alias):
        db = self.settings[alias]
        backend = load_backend(db["ENGINE"])
        return backend.DatabaseWrapper(db, alias)

需要注意此处的settings是BaseConnectionHandler中申明的一个类属性

    @cached_property
    def settings(self):
        self._settings = self.configure_settings(self._settings)
        return self._settings

也就是说, 在建立连接之前, 会先调用configure_settings方法, 而ConnectionHandler中又对configure_settings方法进行了重载, 也就是在重载中, 完成了对组件的加载

    def configure_settings(self, databases):
        databases = super().configure_settings(databases)
        if databases == {}:
            databases[DEFAULT_DB_ALIAS] = {"ENGINE": "django.db.backends.dummy"}
        elif DEFAULT_DB_ALIAS not in databases:
            raise ImproperlyConfigured(
                f"You must define a '{DEFAULT_DB_ALIAS}' database."
            )
        elif databases[DEFAULT_DB_ALIAS] == {}:
            databases[DEFAULT_DB_ALIAS]["ENGINE"] = "django.db.backends.dummy"

        # Configure default settings.
        for conn in databases.values():
            conn.setdefault("ATOMIC_REQUESTS", False)
            conn.setdefault("AUTOCOMMIT", True)
            conn.setdefault("ENGINE", "django.db.backends.dummy")
            if conn["ENGINE"] == "django.db.backends." or not conn["ENGINE"]:
                conn["ENGINE"] = "django.db.backends.dummy"
            conn.setdefault("CONN_MAX_AGE", 0)
            conn.setdefault("CONN_HEALTH_CHECKS", False)
            conn.setdefault("OPTIONS", {})
            conn.setdefault("TIME_ZONE", None)
            for setting in ["NAME", "USER", "PASSWORD", "HOST", "PORT"]:
                conn.setdefault(setting, "")

            test_settings = conn.setdefault("TEST", {})
            default_test_settings = [
                ("CHARSET", None),
                ("COLLATION", None),
                ("MIGRATE", True),
                ("MIRROR", None),
                ("NAME", None),
            ]
            for key, value in default_test_settings:
                test_settings.setdefault(key, value)
        return databases

也就是说对上面所有的connection调用的方法,都会先去hander里面去找, 有没有对应的连接, 没有的话, 则会新建连接, 供ConnectionProxy的__getattr__方法使用, 然后对ConnectionProxy实例也就是connection调用的任何属性或者方法, 都会去上面Connectionhandler中建立的连接实例的相关方法。

以上即为django通过proxy代理模式, 对数据库多组件连接的处理, 通过这种巧妙的方式, 在我看来好处有二,一为懒加载, 不需要的话, 就不会真的去建立连接, 只引入connection的话, 其实引入的是proxy对象, 直到调用方法的时候, 才真正的建立了连接, 并调用连接实例的方法, 其二是通过这种方式, 把对不同的组件的加载抽离成了配置, 而且针对不同的数据库特性和业务之间做到了组件间的分离, 在业务中不需要太过关心数据库组件操作的相关事宜。

其实django源码中海油很多很不错的东西, 比如LazySettings懒加载, 还有数据库迁移中的基于双向链表的算法, 还是很值得一看的。

如果大家对django源码感兴趣, 而功力又不到的话, 可以上B站搜索沈奇才的django源码教程, 相对来说,讲得很细, 可供大家参考。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值