web开发学习(4)- Django数据库连接、游标(bootcamp & progresql)

一、前情回顾

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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值