web开发学习(2) - 从数据库迁移开始(bootcamp)
一. 内容背景:
在启动服务器前,需要创建数据库,进行数据库的迁移,通常执行以下命令; 本文主要从这条命令开始,顺藤摸瓜。
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)相关推荐
- 【Java Web开发学习】Spring4条件化的bean
[Java Web开发学习]Spring4条件化的bean 转载:https://www.cnblogs.com/yangchongxing/p/9071960.html Spring4引入了@Con ...
- web开发 学习_是否想学习Web开发但不知道从哪里开始?
web开发 学习 by Rick West 由里克·韦斯特(Rick West) 是否想学习Web开发但不知道从哪里开始? (Want to learn web development but don ...
- java springmvc https_【Java Web开发学习】Spring MVC 使用HTTP信息转换器
[Java Web开发学习]Spring MVC 使用HTTP信息转换器 @ResponseBody和@RequestBody是启用消息转换的一种简洁和强大方式 消息转换(message conver ...
- c#arcgis engine开发_湖南web开发学习网站要多久
湖南web开发学习网站要多久第13章命令模式(Command)1. 命令模式的关键命令模式的关键之处就是把请求封装成为对象,也就是命 令对象,并定义了统一的执行操作的接口,这个命令对象可以被存储.转发 ...
- 【Java Web开发学习】Spring MVC 拦截器HandlerInterceptor
[Java Web开发学习]Spring MVC 拦截器HandlerInterceptor 转载:https://www.cnblogs.com/yangchongxing/p/9324119.ht ...
- 大数据开发学习:NoSQL数据库入门
大数据处理,涉及到从数据获取到数据存储.数据计算的诸多环节,各个环节需要解决的问题不同,相关岗位要求的技能也不同.在数据存储阶段,对数据库选型是非常重要的一项工作.今天的大数据开发学习分享,我们就来聊 ...
- Laravel学习笔记四-数据库迁移和模型文件
本节将学习使用artisan命令数据库迁移建表,模型文件的学习. 一.数据库迁移 二.模型文件 三.git checkout -f 命令应用 我们在开发中经常会遇到这样的情况,如果一个分支中提交了一个 ...
- Web 开发学习笔记(1) --- 搭建你的第一个 Web Server
简介 Flask 是一个轻量级的 Web 框架, 如果要学习 Web 开发, Flask 非常适合作为我们学习的起点. 通过接下来的这一些列的博客, 我们将学习如何利用 Flask 以及其他工具, 搭 ...
- java web开发学习总结
java web 开发基础内容理解 解决中文乱码问题 请求参数乱码 输出乱码 jsp的运行原理 GET请求和POST请求的区别 静态包含和动态包含 静态包含 动态包含 两者的区别 Cookie和Ses ...
最新文章
- python 调用 javascript函数
- SpringMVC源码阅读:过滤器
- powershell 查看WMI信息和几个WMI类示例
- java 原理图_Java中比较重要的原理图(三大框架、、、、)
- Jetson TX2板载相机opencv调用打开
- Android常见的内存泄漏分析
- MySQL 加锁处理分析(二)
- 箫 音之乐 生(声)之乐
- Word 2003特殊符号录入与编辑(转)
- 图片像素对比OpenCV实现,实现人工分割跟算法分割图像结果的对比
- trigger 根据绑定到匹配元素的给定的事件类型执行所有的处理程序和行为。
- C++之priority_queue
- Lodop打印控件介绍
- mysql dba高级教程_MySQL DBA高级视频教程 博瑞森一线DBA大神亲授
- 《后端从入门到熟悉-序言》
- Mac上的Redis客户端 G-dis
- 华为Android 10手机微信小程序无法调起的问题解决办法
- 定义表格标签(table)
- PGM 格式图像数据操作
- Java Springboot切面+注解实现数据脱敏