一. 内容背景:

在启动服务器前,需要创建数据库,进行数据库的迁移,通常执行以下命令; 本文主要从这条命令开始,顺藤摸瓜。

python manage.py migrate

二. 主要的线索:

1. 入口:./bootcamp/bootcamp/manage.py

#设置环境变量:让os.environ['DJANGO_SERRINGS_MODULE'] = ./config/settings/local.py
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings.local')current_path = os.path.dirname(os.path.abspath(__file__))  #获取当前manage.py文件的目录:./bootcamp/boocamp/
sys.path.append(os.path.join(current_path, 'bootcamp'))   #设置项目的路径:./bootcamp/bootcamp/bootcampexecute_from_command_line(sys.argv)    #1. 获取命令行参数,执行函数,转

2. 执行execute_from_command_line(sys.argv)

目录:./bootcamp/ll_env/lib/python3.7/site-packages/django/core/management/__init__.py

def execute_from_command_line(argv=None):"""Run a ManagementUtility."""utility = ManagementUtility(argv)utility.execute()

3. 执行execute()函数

目录:./bootcamp/ll_env/lib/python3.7/site-packages/django/core/management/__init__.py

class ManagementUtility:#def fetch_command(self, subcommand):#def autocomplete(self):def execute(self):try:subcommand = self.argv[1]   #获取命令行参数,此时subcommand = migrateexcept IndexError:subcommand = 'help'  # Display help if no arguments were given.#以下是检测命令行参数相关的代码parser = CommandParser(usage='%(prog)s subcommand [options] [args]', add_help=False, allow_abbrev=False)parser.add_argument('--settings')    parser.add_argument('--pythonpath')parser.add_argument('args', nargs='*')  # catch-alltry:options, args = parser.parse_known_args(self.argv[2:]) # 报异常,因为我们的命令行参数只有一个,python manage.py migrate;所以argv[2:]没有值handle_default_options(options)  #当使用了--setting/--pythonpath可选命令时执行except CommandError:pass  # Ignore any option errors at this point.try:settings.INSTALLED_APPS  #当调用用句点'.'访问的对象属性不存在时,将执行该对象的__getattr__()函数except ImproperlyConfigured as exc:self.settings_exception = excexcept ImportError as exc:self.settings_exception = exc

4. 访问settings.INSTALLED_APPS(采用单例设计模式)

目录:./bootcamp/ll_env/lib/python3.7/site-packages/django/conf/__init__.py

目的:settings.INSTALLED_APPS属性的初始化

配置信息的流向:

- global_seting.py  ---》 settings._wraped

- local.py -- 》 settings._wraped

- settings._wraped --》settings.__dict__

#采用单例设计模式:让一个类在项目中只存在一个对象,即使用到这个类的地方很多,也只存在一个对象settings = LazySettings()      # settings.INSTALLED_APPS , settings是LazySettings类的一个实例

LazySetting类

#当调用用句点'.'访问的对象属性不存在时,将执行该对象的__getattr__()函数

class LazySettings(LazyObject): def _setup(self, name=None):settings_module = os.environ.get(ENVIRONMENT_VARIABLE)  #3. 取得第1步设置的环境变量的值:./config/settings/local.pyif not settings_module:desc = ("setting %s" % name) if name else "settings"raise ImproperlyConfigured("Requested %s, but settings are not configured. ""You must either define the environment variable %s ""or call settings.configure() before accessing settings."% (desc, ENVIRONMENT_VARIABLE))self._wrapped = Settings(settings_module)  #4. self._wrapped是Settings类的实例def __getattr__(self, name):       #1. settings.INSTALLED_APPS, name=INSTALLED_APPS"""Return the value of a setting and cache it in self.__dict__."""if self._wrapped is empty:self._setup(name)          #2.从django/conf/global_settings.py文件获取相关配置信息,存于self._wrapped中(一个Settings类),其中就有INSTALLED_APPSval = getattr(self._wrapped, name)   #5. 想要获得Settings类实例的name属性(INSTALLED_APPS)self.__dict__[name] = valreturn val

5. val=getattr(self._wrapped, name)

目录:./bootcamp/ll_env/lib/python3.7/site-packages/django/conf/__init__.py

想要获得Settings类实例的name属性,但是Settings类中,找不到name=INSTALLED_APP的属性,也没有找到__getattr__方法属性,在Setting类初始化过程找找线索:setattr(self, setting, getattr(global_setting,setting))

语法:setattr(object,name,value)   等价于 set object.name=value

所以,self.setting=getattr(global_setting,setting),在global的配置文件中存在INSTALLED_APP,这样在回到第4步的val,

val=getattr(global_setting,setting), 相当于找到global_seting中的INSTALLED_APP的值,最后再返回到第3步

class Settings:def __init__(self, settings_module):    #setting_module is envpath# update this dict from global settings (but only for ALL_CAPS settings)for setting in dir(global_settings):if setting.isupper():setattr(self, setting, getattr(global_settings, setting))   # sytax: setattr(object,name,value)   euqal to set object.name=value#self.setting=getattr(global_setting,setting)   #获取global_setting的配置信息# store the settings module in case someone later caresself.SETTINGS_MODULE = settings_modulemod = importlib.import_module(self.SETTINGS_MODULE)     #import the moudle /bootcamp/config/settings/local.pytuple_settings = ("INSTALLED_APPS","TEMPLATE_DIRS","LOCALE_PATHS",)self._explicit_settings = set()     #create a no duplicate set(a kind of collections)for setting in dir(mod):if setting.isupper():setting_value = getattr(mod, setting)if (setting in tuple_settings andnot isinstance(setting_value, (list, tuple))):raise ImproperlyConfigured("The %s setting must be a list or a tuple. " % setting)setattr(self, setting, setting_value)    #用来自bootcamp/conf/settings/local.py文件的信息更新Setting类中的Setting中的配置信息self._explicit_settings.add(setting)

6. setting.INSTALLED_APPS里面有那些内容呢

目录:bootcamp/conf/settings/local.py

local.py文件的中INSTALLED_APPS的配置信息还使用了同文件夹下base.py的数据,可能还有其他的重要的配置信息,所以此时的settings.INSTALLED已经包含包含列很多信息了,只是我还不知道有什么用

#文件  bootcamp/conf/settings/local.py  部分代码省略
from .base import *  # noqa
INSTALLED_APPS += ['debug_toolbar']  # noqa F405
INSTALLED_APPS += ['django_extensions']  # noqa F405#文件 bootcamp/conf/settings/base.py# APPS
# ------------------------------------------------------------------------------
DJANGO_APPS = ['django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.sites','django.contrib.messages','django.contrib.staticfiles','django.contrib.humanize','django.contrib.admin','django.forms',
]
THIRD_PARTY_APPS = ['crispy_forms','sorl.thumbnail','allauth','allauth.account','allauth.socialaccount',# 'allauth.socialaccount.providers.amazon',# 'allauth.socialaccount.providers.github',# 'allauth.socialaccount.providers.google',# 'allauth.socialaccount.providers.linkedin',# 'allauth.socialaccount.providers.slack','channels','django_comments','graphene_django','markdownx','taggit',
]
LOCAL_APPS = ['bootcamp.users.apps.UsersConfig',# Your stuff: custom apps go here'bootcamp.articles.apps.ArticlesConfig','bootcamp.messager.apps.MessagerConfig','bootcamp.news.apps.NewsConfig','bootcamp.notifications.apps.NotificationsConfig','bootcamp.qa.apps.QaConfig','bootcamp.search.apps.SearchConfig'
]
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS

7. 执行django.setup (从第3步到这边)

目录:

(1) ./bootcamp/ll_env/lib/python3.7/site-packages/django/core/management/__init__.py

(2) django/__init__()     #这是django.setup()代码所在的文件

 # 文件./bootcamp/ll_env/lib/python3.7/site-packages/django/core/management/__init__.pyif settings.configured:if subcommand == 'runserver' and '--noreload' not in self.argv:#此处代码省略......else:django.setup()    # 第一步self.autocomplete()      #第二步if subcommand == 'help':#次处代码省略......elif subcommand == 'version' or self.argv[1:] == ['--version']:sys.stdout.write(django.get_version() + '\n')elif self.argv[1:] in (['--help'], ['-h']):sys.stdout.write(self.main_help_text() + '\n')else:self.fetch_command(subcommand).run_from_argv(self.argv)   #第三步

 django.setup()  --》 apps.populate(settings.INSTALLED_APPS) 执行进行APPS的注册

#文件  django/__init__()
def setup(set_prefix=True):configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)if set_prefix:set_script_prefix('/' if settings.FORCE_SCRIPT_NAME is None else settings.FORCE_SCRIPT_NAME)apps.populate(settings.INSTALLED_APPS)

8. apps注册(也采用单例设计模式)

目录:django/apps/__init__.py    django/apps/config.py      django/apps/registry.py

#文件 django/__init__.py         注意这边的设置,有点奇怪,还是没有理解透彻,先跳过
from .config import AppConfig
from .registry import apps__all__ = ['AppConfig', 'apps']

采用单例设计模式 

class Apps:#省略部分代码def __init__(self, installed_apps=()):#---省略代码-----def populate(self, installed_apps=None): #---省略代码-----apps = Apps(installed_apps=None)
实际代码部分

目的:

- 创建一个包含已注册app相关配置信息的一个实例对象,这些配置信息与installed_apps中的内容相对应

- 将以上的配置信息绑定到实例对象apps上面

问题:

从apps我们可以获得什么,如何获取?

app.config都包含哪些信息?

class Apps:#省略部分代码和注释def __init__(self, installed_apps=()):self.all_models = defaultdict(OrderedDict)    # self.all_models[key]=ordereddictself.app_configs = OrderedDict()self.stored_app_configs = []self.apps_ready = self.models_ready = self.ready = Falseself.ready_event = threading.Event()self._lock = threading.RLock()self.loading = Falseself._pending_operations = defaultdict(list)if installed_apps is not None:self.populate(installed_apps)def populate(self, installed_apps=None): if self.ready:returnwith self._lock:   #进入这部分代码if self.ready:returnfor entry in installed_apps:      #entry = 'django_extensions'....if isinstance(entry, AppConfig):app_config = entryelse:app_config = AppConfig.create(entry)    #创建一个包含已注册app相关配置信息的一个实例对象,这些配置信息与installed_apps中的内容相对应if app_config.label in self.app_configs:raise ImproperlyConfigured("Application labels aren't unique, ""duplicates: %s" % app_config.label)self.app_configs[app_config.label] = app_config   #将以上的配置信息绑定到实例对象apps上面app_config.apps = self       # 这一步为什么这么写呢????# Check for duplicate app names.counts = Counter(app_config.name for app_config in self.app_configs.values())duplicates = [name for name, count in counts.most_common() if count > 1]if duplicates:raise ImproperlyConfigured("Application names aren't unique, ""duplicates: %s" % ", ".join(duplicates))self.apps_ready = True# Phase 2: import models modules.for app_config in self.app_configs.values():app_config.import_models()self.clear_cache()self.models_ready = True# Phase 3: run ready() methods of app configs.for app_config in self.get_app_configs():app_config.ready()self.ready = Trueself.ready_event.set()     #将Event的标志设置为True

9.  app_config = AppConfig.create(entry)

目录: django/apps/config.py

AppConfig重点包含了哪些信息:包含本地app文件所在的目录(app_name),以及目录中一些可用的模块(app_module),这部分将绑定在了实例对象apps上面,让系统取用(通过单例设计模式,整个系统只有这一个对象)

class AppConfig:"""Class representing a Django application and its configuration."""def __init__(self, app_name, app_module):  #app_name:实际是app所在的目录,app_modulue实际上是app所在目录下一些可以用的模块self.name = app_nameself.module = app_moduleself.apps = Noneif not hasattr(self, 'label'):self.label = app_name.rpartition(".")[2]  #app_name=bootcamp.articles    self.labe=articlesif not hasattr(self, 'verbose_name'):self.verbose_name = self.label.title()    #self.verbose_name=Articles,首字母大写if not hasattr(self, 'path'):self.path = self._path_from_module(app_module)self.models_module = Noneself.models = None@classmethod        #不用实例化对象就可使用的方法def create(cls, entry):     #entry = 'django_extensions'try:module = import_module(entry)   #将installed_apps的模块(app)导入进来,哪些模块:参考bootcamp/conf/settings/base.py#这边以'bootcamp.articles.apps.ArticlesConfig'为例except ImportError:#...代码省略....else:                                     #这部分代码块主要为了获得配置信息所在的目录,以及配置包含信息类的名字try:entry = module.default_app_config  #死活找不到 default_app_config属性是什么时候加进来的,猜测是创建app的时候,命令startapp,先跳过except AttributeError:# Otherwise, it simply uses the default app config class.return cls(entry, module)else:mod_path, _, cls_name = entry.rpartition('.')  #mod_path=bootcamp.articles.apps  cls_name=ArticlesConfigmod = import_module(mod_path)   #mod_path=bootcamp.articles.appstry:cls = getattr(mod, cls_name)   #获取到包含配置信息的类class ArticlesConfigexcept AttributeError:# ....代码省略......if not issubclass(cls, AppConfig):    #cls=class ArticlesConfigraise ImproperlyConfigured("'%s' isn't a subclass of AppConfig." % entry)try:app_name = cls.name          #获取该app文件所在目录 app_name = bootcamp.articlesexcept AttributeError:raise ImproperlyConfigured("'%s' must supply a name attribute." % entry)# Ensure app_name points to a valid module.try:app_module = import_module(app_name)   #app_name = bootcamp.articlesexcept ImportError:raise ImproperlyConfigured("Cannot import '%s'. Check that '%s.%s.name' is correct." % (app_name, mod_path, cls_name,))# Entry is a path to an app config class.return cls(app_name, app_module)  #根据以上信息实例化一个AppConfigs实例对象,并返回该对象

我的文件夹示例(以bootcamp项目下面的articles这个app为例)

10. 现在要回到第3步,执行下面的命令(上面还有一个关注点是线程的控制,先跳过)

目录:django/core/management/__init__.py

#文件 django/core/management/__init__.pyself.fetch_command(subcommand).run_from_argv(self.argv)

执行fetch_command命令:

返回一个位于django/core/management/comands/migrate.py文件中的Command类的一个实例对象,然后调用run_from_argv(self.argv)

class ManagementUtility:def __init__(self, argv=None):#....代码省略....def autocomplete(self): #....代码省略....def execute(self):#...部分代码省略....self.fetch_command(subcommand).run_from_argv(self.argv)def fetch_command(self, subcommand):           #跳到这里执行  subcommand=migrate"""Try to fetch the given subcommand, printing a message with theappropriate command called from the command line (usually"django-admin" or "manage.py") if it can't be found."""# Get commands outside of try block to prevent swallowing exceptionscommands = get_commands()     #返回django/core/management/commands目录的模块相关信息#commands = {'migrate':'django.core','runserver':'django.core','startapp':'django.core',.....}try:app_name = commands[subcommand]          #app_name='django.core'except KeyError:# ....代码省略....if isinstance(app_name, BaseCommand):# If the command is already loaded, use it directly.klass = app_nameelse:klass = load_command_class(app_name, subcommand)    #执行这里,#app_name='django.core'   subcommand='migrate'#返回一个位于django/core/management/comands/migrate.py文件中的Command类的一个实例对象return klass

11. Command类创建 和 run_from_argv(self.argv)方法调用

目录:django/core/management/commands/migrate.py

Command继承自BaseCommand,它本身并没有run_from_argv(self.argv)方法,而是调用父类BaseCommand的方法

同时他也没有定义初始化方法__init__()

class Command(BaseCommand):#....部分代码省略.......def add_arguments(self, parser):#代码省略@no_translationsdef handle(self, *args, **options):#代码省略
----转到父类BaseCommand

目录:django/core/management/base.py

注意:parser = self.create_parser(argv[0], argv[1])方法里面有一条语句,是调用子类的的方法self.add_arguments(parser):设置了可以命令--database的默认参数,当我们直接输入命令python manage.py migrate时(而不用指定--database database_argument),将采用默认设置

class BaseCommand:#定义了一些全局变量,看不懂,先跳过help = ''_called_from_command_line = Falseoutput_transaction = False  # Whether to wrap the output in a "BEGIN; COMMIT;"requires_migrations_checks = Falserequires_system_checks = Truebase_stealth_options = ('skip_checks', 'stderr', 'stdout')stealth_options = ()def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False):#代码省略.....def create_parser(self, prog_name, subcommand, **kwargs):#代码省略.....def run_from_argv(self, argv):self._called_from_command_line = Trueparser = self.create_parser(argv[0], argv[1])   #注意该方法里面有一条语句,是调用子类的的方法self.add_arguments(parser):设置了可以命令--database的默认参数,当我们直接输入命令python manage.py migrate时(而不用指定--database database_argument),将采用默认设置options = parser.parse_args(argv[2:])   #没看懂,跳过cmd_options = vars(options)  #返回一个与命令行参数相关的字典,其中就有{'database':'default',.......}# Move positional args out of options to mimic legacy optparseargs = cmd_options.pop('args', ())   #args=()handle_default_options(options)      #没有提供额外的可选参数和命令,这里无影响,跳过try:self.execute(*args, **cmd_options)   #最后到这里except Exception as e:#代码省略......finally:try:connections.close_all()except ImproperlyConfigured:#代码省略......

12. 执行父类BaseCommand的execute(*args, **cmd_options)

目录:django/core/management/base.py

有一点疑问:为什么要在子类和父类之间跳来跳去呢,不懂,先跳过 

class BaseCommand:#定义了一些全局变量,看不懂,先跳过help = ''_called_from_command_line = Falseoutput_transaction = False  # Whether to wrap the output in a "BEGIN; COMMIT;"requires_migrations_checks = Falserequires_system_checks = Truebase_stealth_options = ('skip_checks', 'stderr', 'stdout')stealth_options = ()def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False):   #代码省略.....def create_parser(self, prog_name, subcommand, **kwargs):    #代码省略.....def run_from_argv(self, argv):  #代码省略.....def execute(self, *args, **options):#.....省略部分代码..............if self.requires_system_checks and not options.get('skip_checks'):self.check()                         #跳过if self.requires_migrations_checks:self.check_migrations()              #跳过output = self.handle(*args, **options)   #将调用子类Command.handle()方法,其中args={}, options={'database':'default',......}#跳转到子类Command,目录:django/core/management/commands/migrate.pyif output:if self.output_transaction:connection = connections[options.get('database', DEFAULT_DB_ALIAS)]output = '%s\n%s\n%s' % (self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),output,self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),)self.stdout.write(output)return output

--跳转到子类Commmd.handle()方法

目录:django/core/management/commands/migrate.py

class Command(BaseCommand):def add_arguments(self, parser):#...部分代码省略....parser.add_argument('--database',default=DEFAULT_DB_ALIAS,             #该条重要,参考第11步,本步骤于此无关help='Nominates a database to synchronize. Defaults to the "default" database.',)@no_translationsdef handle(self, *args, **options):self.verbosity = options['verbosity']        #跳过self.interactive = options['interactive']    #跳过for app_config in apps.get_app_configs():if module_has_submodule(app_config.module, "management"):    #确认INSTALLED_APPS中app文件目录下是否还有management这个包,如果有的话,就导入这个包import_module('.management', app_config.name) #如果app_config_name=bootcamp/articles, 本句相当于:import bootcamp/articles/management, 但对于articles这个app,management是不存在的# Get the database we're operating fromdb = options['database']        #db='default'  参考第11步 create_parser()connection = connections[db]    #最后,跳转到这里#准备数据库时,后端backend所需要的Hook connection.prepare_database()   #跳过:django/db/base/base.py#.......后续代码省略.......

13. 获取数据库connection = connections[db]

目录:django/db/__init__.py

首先这又是一个单例模式,跳转到ConectionHandler() ,目录:django/db/utils.py

from django.core import signals
from django.db.utils import (DEFAULT_DB_ALIAS, DJANGO_VERSION_PICKLE_KEY, ConnectionHandler,ConnectionRouter, DatabaseError, DataError, Error, IntegrityError,InterfaceError, InternalError, NotSupportedError, OperationalError,ProgrammingError,
)__all__ = ['connection', 'connections', 'router', 'DatabaseError', 'IntegrityError','InternalError', 'ProgrammingError', 'DataError', 'NotSupportedError','Error', 'InterfaceError', 'OperationalError', 'DEFAULT_DB_ALIAS','DJANGO_VERSION_PICKLE_KEY',
]connections = ConnectionHandler()

--跳转到ConnectionHandler

目录:django/db/utils.py

划重点:Threading.local()方法,多线程局部变量,

我的初步理解:多个线程共同使用一个变量名字,但是实际每个线程内部访问该变量数据时,是相互独立的,不会相互干扰

class ConnectionHandler:#--省略部分代码--def __init__(self, databases=None):self._databases = databasesself._connections = local()    #多线程局部变量!!!!!def __getitem__(self, alias): #alias='default' if hasattr(self._connections, alias): return getattr(self._connections, alias)  #在第12步调用Connections[db],跳转到此处 alias='default' #类刚初始化后,self._connections为空,条件不符合,跳到下一步self.ensure_defaults(alias)         #设置一些默认的键值对,在self._database里面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'])  #相当于import django.db.backends.postgresql.baseconn = backend.DatabaseWrapper(db, alias)     #最后跳转到这一步setattr(self._connections, alias, conn)return conn@cached_propertydef databases(self):if self._databases is None:self._databases = settings.DATABASES   #self._databases={ 'default': env.db('DATABASE_URL'),}   bootcamp/config/settings/base.pyif self._databases == {}:self._databases = {DEFAULT_DB_ALIAS: {'ENGINE': 'django.db.backends.dummy',},}if DEFAULT_DB_ALIAS not in self._databases:raise ImproperlyConfigured("You must define a '%s' database." % DEFAULT_DB_ALIAS)if self._databases[DEFAULT_DB_ALIAS] == {}:self._databases[DEFAULT_DB_ALIAS]['ENGINE'] = 'django.db.backends.dummy'return self._databases                     #返回值 { 'default': env.db('DATABASE_URL'),}

14. conn=backend.DatabaseWrapper(db,alias)

目录:django/db/postgresql/base.py

DatabaseWrapper是BaseDatabaseWrapper的子类,且未定义初始化方法__init__()

class DatabaseWrapper(BaseDatabaseWrapper): # ....代码省略....

--跳转至父类BaseDatabaseWrapper,对象实例化

目录:django/db/backends/base/base.py

看不懂,先跳过

class BaseDatabaseWrapper:#全局变量的定义,看不懂有什么用,先跳过"""Represent a database connection."""# Mapping of Field objects to their column types.data_types = {}# Mapping of Field objects to their SQL suffix such as AUTOINCREMENT.data_types_suffix = {}# Mapping of Field objects to their SQL for CHECK constraints.data_type_check_constraints = {}ops = Nonevendor = 'unknown'display_name = 'unknown'SchemaEditorClass = None# Classes instantiated in __init__().client_class = Nonecreation_class = Nonefeatures_class = Noneintrospection_class = Noneops_class = Nonevalidation_class = BaseDatabaseValidationqueries_limit = 9000def __init__(self, settings_dict, alias=DEFAULT_DB_ALIAS)   #初始化位置,settings_dict=db={'NAME': 'bootcamp', 'USER': 'u_bootcamp', 'PASSWORD': 'p4ssw0rd', 'HOST': 'localhost', 'PORT': 5432, 'ENGINE': 'django.db.backends.postgresql', 'ATOMIC_REQUESTS': True, 'AUTOCOMMIT': True, 'CONN_MAX_AGE': 0, 'OPTIONS': {}, 'TIME_ZONE': None, 'TEST': {'CHARSET': None, 'COLLATION': None, 'NAME': None, 'MIRROR': None}}# Connection related attributes.# The underlying database connection.self.connection = None# `settings_dict` should be a dictionary containing keys such as# NAME, USER, etc. It's called `settings_dict` instead of `settings`# to disambiguate it from Django settings modules.self.settings_dict = settings_dictself.alias = alias# Query logging in debug mode or when explicitly enabled.self.queries_log = deque(maxlen=self.queries_limit)self.force_debug_cursor = False# Transaction related attributes.# Tracks if the connection is in autocommit mode. Per PEP 249, by# default, it isn't.self.autocommit = False# Tracks if the connection is in a transaction managed by 'atomic'.self.in_atomic_block = False# Increment to generate unique savepoint ids.self.savepoint_state = 0# List of savepoints created by 'atomic'.self.savepoint_ids = []# Tracks if the outermost 'atomic' block should commit on exit,# ie. if autocommit was active on entry.self.commit_on_exit = True# Tracks if the transaction should be rolled back to the next# available savepoint because of an exception in an inner block.self.needs_rollback = False# Connection termination related attributes.self.close_at = Noneself.closed_in_transaction = Falseself.errors_occurred = False# Thread-safety related attributes.self._thread_sharing_lock = threading.Lock()self._thread_sharing_count = 0self._thread_ident = _thread.get_ident()# A list of no-argument functions to run when the transaction commits.# Each entry is an (sids, func) tuple, where sids is a set of the# active savepoint IDs when this function was registered.self.run_on_commit = []# Should we run the on-commit hooks the next time set_autocommit(True)# is called?self.run_commit_hooks_on_set_autocommit_on = False# A stack of wrappers to be invoked around execute()/executemany()# calls. Each entry is a function taking five arguments: execute, sql,# params, many, and context. It's the function's responsibility to# call execute(sql, params, many, context).self.execute_wrappers = []self.client = self.client_class(self)self.creation = self.creation_class(self)self.features = self.features_class(self)self.introspection = self.introspection_class(self)self.ops = self.ops_class(self)self.validation = self.validation_class(self)

15 . 回到第十三步:setattr(self._connections, alias, conn)

目录:django/db/__init__.py

目的:将上一步获得的数据库连接的对象conn绑定到Connections的实例对象上,可返回步骤12/13查看,由于采用了单例设计模式,系统中只有这一个实例对象,还是不是很懂,这一部分,特别是结合Threading.local()多线程局部变量,后续再回来~

16. 再返回到第12步,继续执行(待续)

--目录django/core/management/commands/migrate.py

self.migration_progress_callback是一个方法,代码将一个方法传递给了一个类,该方法也位于当前目录中

class Command(BaseCommand):#-----部分代码省略-----------@no_translationsdef handle(self, *args, **options):db = options['database']      connection = connections[db]   #单例设计模式,指向一个数据库连接的对象,django/db/postgresql/base.py         connection.prepare_database()  #跳过# Work out which apps have migrations and which do notexecutor = MigrationExecutor(connection, self.migration_progress_callback)  #self.migration_progress_callback是一个方法,代码将一个方法传递给了一个类# Raise an error if any migrations are applied before their dependencies.executor.loader.check_consistent_history(connection)

--目录:django/db/migration/executor

class MigrationExecutor:     #这可能是一个具有数据库迁移功能的类def __init__(self, connection, progress_callback=None):self.connection = connection        #获取数据的连接对象self.loader = MigrationLoader(self.connection)self.recorder = MigrationRecorder(self.connection)self.progress_callback = progress_callback

--目录:django/db/migration/loader

MigrationLoader类的作用是:从磁盘加载迁移文件,并且从数据库加载迁移文件的状态

在app的目录下,有一个migration的文件的夹,在类初始化时,通常会扫描该文件夹,搜索一个叫Migration的类,该类继承自django.db.migrations.Migration

class MigrationLoader:def __init__(self, connection, load=True, ignore_no_migrations=False):self.connection = connectionself.disk_migrations = Noneself.applied_migrations = Noneself.ignore_no_migrations = ignore_no_migrationsif load:self.build_graph()

--目录:django/db/migration/recorder

MigrationRecorder类主要负责将迁移的记录存储到数据库中

class MigrationRecorder:_migration_class = Nonedef __init__(self, connection):self.connection = connection

完成各迁移相关类的初始化后,执行命令:executor.loader.check_consistent_history(connection)

check_consistent_history()方法位于目录:django/db/migration/loader(MigrationLoader类内部)

class MigrationLoader:def __init__(self, connection, load=True, ignore_no_migrations=False):self.connection = connectionself.disk_migrations = Noneself.applied_migrations = Noneself.ignore_no_migrations = ignore_no_migrationsif load:self.build_graph()def check_consistent_history(self, connection):recorder = MigrationRecorder(connection)     #获取该连接相关的迁移记录applied = recorder.applied_migrations()      #跳到这里#部分代码省略---

执行:applied = recorder.applied_migration()

目录:django/db/migration/recorder

def applied_migrations(self):if self.has_table():return {tuple(x) for x in self.migration_qs.values_list('app', 'name')}   #运行这里else:return set()@property
def migration_qs(self):return self.Migration.objects.using(self.connection.alias)   在运行这里,为什么找不到objects啊????

首先Migration是类对象,通过元类BaseMigration创建对象,他同时是Model的子类,Model类对象也通过元类BaseMigration创建

  • _meta什么时候加入的?
  • self.Migration.objects哪里找?

目录:django/db/migrations/models/base.py

class ModelBase(type):"""Metaclass for all models."""def __new__(cls, name, bases, attrs, **kwargs):super_new = super().__new__     #super_new=type.__new__# Also ensure initialization is only performed for subclasses of Model# (excluding Model class itself).parents = [b for b in bases if isinstance(b, ModelBase)]    #for class Migration, parents = Model#for class Model, parents = []if not parents:return super_new(cls, name, bases, attrs)# Create the class. 部分代码省略new_class = super_new(cls, name, bases, new_attrs, **kwargs)   #最后将返回这个类对象,也就是Migration#----部分代码省略----new_class.add_to_class('_meta', Options(meta, app_label))  #add_to_class方法在元类BaseModel里面定义,_meta在这里被引入到Mmigration中#----部分代码省略----new_class._prepare()new_class._meta.apps.register_model(new_class._meta.app_label, new_class)return new_class        #new_class就是最后创建的Migration类对象
#django/db/migrations/models/base.py
def _prepare(cls):"""Create some methods once self._meta has been populated."""opts = cls._meta          #cls=new_classopts._prepare(cls)        #这步是把一个autoField的类绑定到我们的Migration类对象上#django/models/options.py if not opts.managers:    #opts.managers=Noneif any(f.name == 'objects' for f in opts.fields):raise ValueError(‘此处代码省略’)manager = Manager()                       #django/db/migrations/models/manager.py !!!!!!!!!manager.auto_created = Truecls.add_to_class('objects', manager)      #objects就是在这里引入啊!!!!!!!!!!,他指向了manager类class_prepared.send(sender=cls)        #没看懂,先跳过

17. Manager对象

#文件 django/db/models/manager.py
class Manager(BaseManager.from_queryset(QuerySet)):    #class Manager(BaseManagerFromQuerySet)pass#文件 django/db/models/manager.py
class BaseManager:#......部分代码省略.....@classmethoddef from_queryset(cls, queryset_class, class_name=None):if class_name is None:class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)  #class_name = BaseManagerFromQuerySetreturn type(class_name, (cls,), {          #type创建类的语法:type(name,bases,attrs)                      '_queryset_class': queryset_class,     #创建一个BaseManagerFromQuerySet类,该类继承BaseManager,并绑定QuerySet类**cls._get_queryset_methods(queryset_class), #没看懂,可能是绑定一些方法})#文件 django/db/models/query.py
class QuerySet:"""Represent a lazy database lookup for a set of objects."""def __init__(self, model=None, query=None, using=None, hints=None):self.model = modelself._db = usingself._hints = hints or {}self.query = query or sql.Query(self.model)self._result_cache = Noneself._sticky_filter = Falseself._for_write = Falseself._prefetch_related_lookups = ()self._prefetch_done = Falseself._known_related_objects = {}  # {rel_field: {pk: rel_obj}}self._iterable_class = ModelIterableself._fields = Nonedef _chain(self, **kwargs):"""Return a copy of the current QuerySet that's ready for anotheroperation."""obj = self._clone()if obj._sticky_filter:obj.query.filter_is_sticky = Trueobj._sticky_filter = Falseobj.__dict__.update(kwargs)return objdef _clone(self):"""Return a copy of the current QuerySet. A lightweight alternativeto deepcopy()."""c = self.__class__(model=self.model, query=self.query.chain(), using=self._db, hints=self._hints)c._sticky_filter = self._sticky_filterc._for_write = self._for_writec._prefetch_related_lookups = self._prefetch_related_lookups[:]c._known_related_objects = self._known_related_objectsc._iterable_class = self._iterable_classc._fields = self._fieldsreturn cdef using(self, alias):"""Select which database this QuerySet should execute against."""clone = self._chain()clone._db = aliasreturn clone

18. self.Migration.objects.using(self.connection.alias)

目录:django/db/migration/recorder

获取QuerySet的一份拷贝,其中_db值用self.connection.alias替换

#文件 django/db/migration/recorder
@property
def migration_qs(self):                    return self.Migration.objects.using(self.connection.alias)    #获取QuerySet的一份拷贝,其中_db值用self.connection.alias替换def applied_migrations(self):"""Return a set of (app, name) of applied migrations."""if self.has_table():return {tuple(x) for x in self.migration_qs.values_list('app', 'name')}  #values_list是QuerySet的方法else:# If the django_migrations table doesn't exist, then no migrations# are applied.return set()

只能到这了,搞不动。。。。。。。。。。。。。。。。。。。

web开发学习(2) - 从数据库迁移开始(bootcamp)相关推荐

  1. 【Java Web开发学习】Spring4条件化的bean

    [Java Web开发学习]Spring4条件化的bean 转载:https://www.cnblogs.com/yangchongxing/p/9071960.html Spring4引入了@Con ...

  2. web开发 学习_是否想学习Web开发但不知道从哪里开始?

    web开发 学习 by Rick West 由里克·韦斯特(Rick West) 是否想学习Web开发但不知道从哪里开始? (Want to learn web development but don ...

  3. java springmvc https_【Java Web开发学习】Spring MVC 使用HTTP信息转换器

    [Java Web开发学习]Spring MVC 使用HTTP信息转换器 @ResponseBody和@RequestBody是启用消息转换的一种简洁和强大方式 消息转换(message conver ...

  4. c#arcgis engine开发_湖南web开发学习网站要多久

    湖南web开发学习网站要多久第13章命令模式(Command)1. 命令模式的关键命令模式的关键之处就是把请求封装成为对象,也就是命 令对象,并定义了统一的执行操作的接口,这个命令对象可以被存储.转发 ...

  5. 【Java Web开发学习】Spring MVC 拦截器HandlerInterceptor

    [Java Web开发学习]Spring MVC 拦截器HandlerInterceptor 转载:https://www.cnblogs.com/yangchongxing/p/9324119.ht ...

  6. 大数据开发学习:NoSQL数据库入门

    大数据处理,涉及到从数据获取到数据存储.数据计算的诸多环节,各个环节需要解决的问题不同,相关岗位要求的技能也不同.在数据存储阶段,对数据库选型是非常重要的一项工作.今天的大数据开发学习分享,我们就来聊 ...

  7. Laravel学习笔记四-数据库迁移和模型文件

    本节将学习使用artisan命令数据库迁移建表,模型文件的学习. 一.数据库迁移 二.模型文件 三.git checkout -f 命令应用 我们在开发中经常会遇到这样的情况,如果一个分支中提交了一个 ...

  8. Web 开发学习笔记(1) --- 搭建你的第一个 Web Server

    简介 Flask 是一个轻量级的 Web 框架, 如果要学习 Web 开发, Flask 非常适合作为我们学习的起点. 通过接下来的这一些列的博客, 我们将学习如何利用 Flask 以及其他工具, 搭 ...

  9. java web开发学习总结

    java web 开发基础内容理解 解决中文乱码问题 请求参数乱码 输出乱码 jsp的运行原理 GET请求和POST请求的区别 静态包含和动态包含 静态包含 动态包含 两者的区别 Cookie和Ses ...

最新文章

  1. python 调用 javascript函数
  2. SpringMVC源码阅读:过滤器
  3. powershell 查看WMI信息和几个WMI类示例
  4. java 原理图_Java中比较重要的原理图(三大框架、、、、)
  5. Jetson TX2板载相机opencv调用打开
  6. Android常见的内存泄漏分析
  7. MySQL 加锁处理分析(二)
  8. 箫 音之乐 生(声)之乐
  9. Word 2003特殊符号录入与编辑(转)
  10. 图片像素对比OpenCV实现,实现人工分割跟算法分割图像结果的对比
  11. trigger 根据绑定到匹配元素的给定的事件类型执行所有的处理程序和行为。
  12. C++之priority_queue
  13. Lodop打印控件介绍
  14. mysql dba高级教程_MySQL DBA高级视频教程 博瑞森一线DBA大神亲授
  15. 《后端从入门到熟悉-序言》
  16. Mac上的Redis客户端 G-dis
  17. 华为Android 10手机微信小程序无法调起的问题解决办法
  18. 定义表格标签(table)
  19. PGM 格式图像数据操作
  20. Java Springboot切面+注解实现数据脱敏

热门文章

  1. python输出由*组成的正方形_python 输出正方形
  2. Spring全家桶及思维导图
  3. “论文”相关的基本知识(ISI 核心期刊 期刊文献论文)
  4. arduino+屏幕+短信+土壤湿度传感器
  5. 灰太狼的优点,现代好男人的楷模。
  6. matlab实现图片加亮、灰度、取反、增强对比度操作
  7. 我,10年程序员,转行做了项目经理……
  8. pert图java_Gantt图和PERT图
  9. 「设计模式」六大原则之三:里氏替换原则小结
  10. 网站是依靠哪些技术跟踪监视用户的?