一、前情回顾
1. 单例设计模式 & 多线程局部变量
connection = connections[db] # django/core/management/commands/migrate.py
Connections = ConnectHandler() #数据属性: self._database self.__connections=local()
#django/db/utils.py
connection 和 connections并非同一类型,connection指向了负责与数据库连接的实例对象:DatabaseWrapper,它是BaseDatabaseWrapper的子类
2. DatabaseWrapper和BaseDatabaseWrapper的属性和方法,可以通过connection来直接访问,比如下面这段代码:
代码定义了一个MigrationRecorder类,主要负责将迁移文件的记录存储到数据库中
这边关注两点:
- self.connection.introspection.table_names()
- self.connection.cursor()
文件路径:django/db/migrations/recorder.py
class MigrationRecorder:
#--部分代码省略--
_migration_class = None
def __init__(self, connection):
self.connection = connection
def has_table(self):
"""Return True if the django_migrations table exists."""
return self.Migration._meta.db_table in self.connection.introspection.table_names(self.connection.cursor())
self.connection.introspection.table_names()
说明:类名可以用于赋值,相当于获得了类的别名; 调用子类初始化方法
#文件 django/db/backends/postgresql/base.py
class DatabaseWrapper(BaseDatabaseWrapper):
#--部分代码省略--
from .introspection import DatabaseIntrospection
introspection_class = DatabaseIntrospection #类名可以用于赋值
#文件 django/db/backends/base/base.py
class BaseDatabaseWrapper:
#--部分代码省略--
introspection_class = None
def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS):
self.introspection = self.introspection_class(self)
二、游标
1. 游标在哪里?
执行connectons[db]时,实际上是执行以下代码,
def __getitem__(self, alias): #alias='default'
if hasattr(self._connections, alias):
return getattr(self._connections, alias)
self.ensure_defaults(alias)
self.prepare_test_settings(alias)
db = self.databases[alias] #db={env.db('DATABASE_URL'),'NAME':'','USER':'','PASSWORD':'','HOST':'','PORT':'',.....,'ATOMIC_REQUESTS':False,'AUTOCOMMIT':True}
backend = load_backend(db['ENGINE']) #equal to : import django.db.backends.postgresql.base
conn = backend.DatabaseWrapper(db, alias) #import django.db.backends.postgresql.base.DatabaseWrapper(db,alias)
setattr(self._connections, alias, conn)
return conn
其中的conn = backend.Databend(db, alias)也仅是对获得的DatabaseWrapper进行了初始化,在父类的内部,有一个 self.connection = None(要获取这个属性的话: connection.connection )
#文件 django/db/backends/postgresql/base.py
class DatabaseWrapper(BaseDatabaseWrapper):
def create_cursor(self, name=None):
if name:
# In autocommit mode, the cursor will be used outside of a
# transaction, hence use a holdable cursor.
cursor = self.connection.cursor(name, scrollable=False, withhold=self.connection.autocommit)
else:
cursor = self.connection.cursor()
cursor.tzinfo_factory = utc_tzinfo_factory if settings.USE_TZ else None
return cursor
--父类BaseDatabase Wrapper的cursor()方法
--connection = connections[db]时,connection.connection===None,还未连接
#文件 django/db/backends/base/base.py
class BaseDatabaseWrapper:
#--省略部分代码---
def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS):
self.connection = None #connection = connections[db]时,connection.connection===None,还未连接
self.settings_dict = settings_dict
self.alias = alias
def cursor(self):
return self._cursor()
def _cursor(self, name=None):
self.ensure_connection() #self.connection=None,数据库还未连接,该方法将调用self.connect()
with self.wrap_database_errors:
return self._prepare_cursor(self.create_cursor(name)) #调用子类方法create_cursor()
def ensure_connection(self):
"""Guarantee that a connection to the database is established."""
if self.connection is None:
with self.wrap_database_errors:
self.connect()
def connect(self):
"""Connect to the database. Assume that the connection is closed."""
# Check for invalid configurations.
self.check_settings()
# In case the previous connection was closed while in an atomic block
self.in_atomic_block = False
self.savepoint_ids = []
self.needs_rollback = False
# Reset parameters defining when to close the connection
max_age = self.settings_dict['CONN_MAX_AGE']
self.close_at = None if max_age is None else time.time() + max_age
self.closed_in_transaction = False
self.errors_occurred = False
# Establish the connection
conn_params = self.get_connection_params()
self.connection = self.get_new_connection(conn_params) #连接的入口应该在这里!!!!
self.set_autocommit(self.settings_dict['AUTOCOMMIT'])
self.init_connection_state()
connection_created.send(sender=self.__class__, connection=self)
self.run_on_commit = []
def _prepare_cursor(self, cursor):
"""
Validate the connection is usable and perform database cursor wrapping.
"""
self.validate_thread_sharing()
if self.queries_logged:
wrapped_cursor = self.make_debug_cursor(cursor)
else:
wrapped_cursor = self.make_cursor(cursor)
return wrapped_cursor
3. 连接数据库的入口:self.get_new_connection(conn_params)
#文件 django/db/backends/base/base.py
def get_connection_params(self):
settings_dict = self.settings_dict #setting_dice = db = {'USER':'XX','ENGINE':'XX',,,,,}
if settings_dict['NAME'] == '':
#代码省略......
if len(settings_dict['NAME'] or '') > self.ops.max_name_length():
#代码省略......
conn_params = { #conn_params={'database':'bootcamp'}
'database': settings_dict['NAME'] or 'postgres',
**settings_dict['OPTIONS'], #这种写法????
#截取‘NAME’的键值对留下来,其他放在setting_dict的字典中
}
conn_params.pop('isolation_level', None)
if settings_dict['USER']:
conn_params['user'] = settings_dict['USER']
if settings_dict['PASSWORD']:
conn_params['password'] = settings_dict['PASSWORD']
if settings_dict['HOST']:
conn_params['host'] = settings_dict['HOST']
if settings_dict['PORT']:
conn_params['port'] = settings_dict['PORT']
return conn_params #conn_params={'database':'bootcamp','USER':'XX',.....}
def get_new_connection(self, conn_params): #连接数据库的入口!!!!!!!!!!!!!!!
connection = Database.connect(**conn_params) #该文件导入类psycopg2 import psycopg2 as Database
# self.isolation_level must be set:
# - after connecting to the database in order to obtain the database's
# default when no value is explicitly specified in options.
# - before calling _set_autocommit() because if autocommit is on, that
# will set connection.isolation_level to ISOLATION_LEVEL_AUTOCOMMIT.
options = self.settings_dict['OPTIONS']
try:
self.isolation_level = options['isolation_level']
except KeyError:
self.isolation_level = connection.isolation_level
else:
# Set the isolation level to the value from OPTIONS.
if self.isolation_level != connection.isolation_level:
connection.set_session(isolation_level=self.isolation_level)
return connection
4. 再回顾以下
- connetion = connections[db] #获得了一个可以和数据库连接的初始化对象
- connection.connect() #与数据库建立真正的连接(通过psycopg2模块)
- 后面就可以通过获得的connection.connection对象,利用psycopg2模块对数据库进行相关操作了
5. 还是回到游标cursor
#文件 django/db/backends/postgresql/base.py
class DatabaseWrapper(BaseDatabaseWrapper):
def create_cursor(self, name=None):
if name:
# In autocommit mode, the cursor will be used outside of a
# transaction, hence use a holdable cursor.
cursor = self.connection.cursor(name, scrollable=False, withhold=self.connection.autocommit)
else:
cursor = self.connection.cursor()
cursor.tzinfo_factory = utc_tzinfo_factory if settings.USE_TZ else None
return cursor
如何获得游标:
connection = connections[db]
connectioin.cursor() # 游标的获取,实际上经过了许多步骤
步骤:
- 调用父类cursor()方法
- 调用父类_cursor()方法
- 调用父类ensure_connection()方法,连接数据库(connect() get_connection_params() get_new_connection())
- 调用调用psycopg2模块的connect()方法连接数据库,并返回对象绑定到connection.connectiond上面
- 调用子类create_crusor()方法
- 调用connenction.connection.cursor(), 即调用psycopg2模块的cursor()方法并返回给下一步
- 调用父类__prepare_cursor()方法
- 调用父类make_cursor() 或者 make_debug_cursor()方法并返回
在执行 python manage.py migrate进行数据库迁移过程中,executor.loader.check_consistent_history(connection)语句正是在获取数据游标的过程中,获得了与数据的连接(目录:django/core/management/commands/migrate.py)