django db backends探索

由于需要解决django db长连接的问题,最近看了看django db backend相关实现,以及ORM。

一、django db 结构

django.db是django ORM的封装,主要由两部分构成:

  1. django.db.models。
  2. django.db.backends。

代码具体位置在在/usr/local/lib/python2.7/dist-packages/django/db。一般使用django db的方式:

from django.db import connection
cursor = connection.cursor()
cursor.execute('select sleep(1);');

其中

connection

定义在 django/db/__init__.py中:

connections = ConnectionHandler(settings.DATABASES)
router = ConnectionRouter(settings.DATABASE_ROUTERS)
connection = connections[DEFAULT_DB_ALIAS]
backend = load_backend(connection.settings_dict['ENGINE'])
 

二、db backend结构

1.base backend

backend 属于django.db的子目录,在django.db.backends/__init__.py定义 all backend-specific,里面有一些比较重要的类:

BaseDatabaseWrapper               Represents a database connection,一些比较重要的数据库连接操作,如cursor(),close()在这个类中定义。

BaseDatabaseFeatures              一些特性开关,比如是否supports_unspecified_pk

BaseDatabaseOperations         This class encapsulates all backend-specific differences

BaseDatabaseIntrospection         This class encapsulates all backend-specific introspection utilities

BaseDatabaseClient                     This class encapsulates all backend-specific methods for opening a client

BaseDatabaseValidation              验证类,由具体的backend实现。

2.mysql backend

mysql backend只有如下6个文件,大部分类是继承于backends中定义的Base:
client.py               BaseDatabaseClient        继承于 BaseDatabaseClient,实现了runshell()
compiler.py          SQLCompiler                     继承于django.db.models.sql.compiler.SQLCompiler
creation.py           DatabaseCreation           This dictionary maps Field objects to their associated MySQL column
introspection.py  DatabaseIntrospection
validation.py        DatabaseValidation         There are some field length restrictions for MySQL

base.py  很重要的pakcage,定义了mysql的一些重要的类:

DatabaseWrapper           mysql database connection,继承于BaseDatabaseWrapper

CursorWrapper                A thin wrapper around MySQLdb's normal cursor class

DatabaseFeatures          继承于BaseDatabaseFeatures

DatabaseOperations     继承于 BaseDatabaseOperations

三、mongo db util

util.py 定义了db一些公共方法和类,被db.__init__.py调用

load_backend()

ConnectionHandler

ConnectionRouter

四、一些疑问和答案

1.Connection是如何被创建和销毁的?

答案:Connection通过BaseDatabaseWrapper 包装,每次调用_cursor()会新创建一个连接,mysql的实现如下:

def _cursor(self):
        if not self._valid_connection():
            kwargs = {
                'conv': django_conversions,
                'charset': 'utf8',
                'use_unicode': True,
            }
            settings_dict = self.settings_dict
            if settings_dict['USER']:
                kwargs['user'] = settings_dict['USER']
            if settings_dict['NAME']:
                kwargs['db'] = settings_dict['NAME']
            if settings_dict['PASSWORD']:
                kwargs['passwd'] = settings_dict['PASSWORD']
            if settings_dict['HOST'].startswith('/'):
                kwargs['unix_socket'] = settings_dict['HOST']
            elif settings_dict['HOST']:
                kwargs['host'] = settings_dict['HOST']
            if settings_dict['PORT']:
                kwargs['port'] = int(settings_dict['PORT'])
            # We need the number of potentially affected rows after an
            # "UPDATE", not the number of changed rows.
            kwargs['client_flag'] = CLIENT.FOUND_ROWS
            kwargs.update(settings_dict['OPTIONS'])
            self.connection = Database.connect(**kwargs)
            self.connection.encoders[SafeUnicode] = self.connection.encoders[unicode]
            self.connection.encoders[SafeString] = self.connection.encoders[str]
            connection_created.send(sender=self.__class__, connection=self)
        cursor = CursorWrapper(self.connection.cursor())
        return cursor

其中

 self.connection = Database.connect(**kwargs)

就是在初始化Connection,然后返回一个CursorWrapper。close定义在BaseDatabaseWrapper()中,被db.__init__.py中的close()方法调用,代码如下

def close(self):
        if self.connection is not None:
            self.connection.close()
            self.connection = None


2.一条sql语句如何执行?

答案:通过CursorWrapper,调用其exucute()。

3.如何让django 数据库保持长连接?

答案:db.__init__.py定义了connection,在每次请求结束之后close():

# Register an event that closes the database connection
# when a Django request is finished.
def close_connection(**kwargs):
    for conn in connections.all():
        conn.close()
signals.request_finished.connect(close_connection)

如果不希望Django在每次请求结束以后都关闭所有的连接,可以将上述最后一行代码注释。

经过测试,这样是可以达到保持连接的要求 了。但是这种修改Django内部代码的方式过于霸道了,所以继续研究和最后一行代码相关的Signal对象,发现其中还有一个disconnect方 法,对应实现取消信号关联,所以可以采用在django项目中的settings.py文件加入如下代码来实现保持数据库长连接的非非霸道操作。

    from django.core import signals  
    from django.db import close_connection  
      
    # 取消信号关联,实现数据库长连接  
    signals.request_finished.disconnect(close_connection)  
 

猜你喜欢

转载自san-yun.iteye.com/blog/1757027
DB
今日推荐