由于需要解决django db长连接的问题,最近看了看django db backend相关实现,以及ORM。
django.db是django ORM的封装,主要由两部分构成:
代码具体位置在在/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'])
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实现。
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
util.py 定义了db一些公共方法和类,被db.__init__.py调用
load_backend()
ConnectionHandler
ConnectionRouter
答案: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
答案:通过CursorWrapper,调用其exucute()。
答案: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)