在启动服务器前,需要创建数据库,进行数据库的迁移,通常执行以下命令; 本文主要从这条命令开始,顺藤摸瓜。
python manage.py migrate
#设置环境变量:让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/bootcamp
execute_from_command_line(sys.argv) #1. 获取命令行参数,执行函数,转
目录:./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()
目录:./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 = migrate
except 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-all
try:
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 = exc
except ImportError as exc:
self.settings_exception = exc
目录:./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.py
if 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_APPS
val = getattr(self._wrapped, name) #5. 想要获得Settings类实例的name属性(INSTALLED_APPS)
self.__dict__[name] = val
return val
目录:./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 cares
self.SETTINGS_MODULE = settings_module
mod = importlib.import_module(self.SETTINGS_MODULE) #import the moudle /bootcamp/config/settings/local.py
tuple_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 and
not 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)
目录: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
目录:
(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__.py
if 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)
目录: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]=ordereddict
self.app_configs = OrderedDict()
self.stored_app_configs = []
self.apps_ready = self.models_ready = self.ready = False
self.ready_event = threading.Event()
self._lock = threading.RLock()
self.loading = False
self._pending_operations = defaultdict(list)
if installed_apps is not None:
self.populate(installed_apps)
def populate(self, installed_apps=None):
if self.ready:
return
with self._lock: #进入这部分代码
if self.ready:
return
for entry in installed_apps: #entry = 'django_extensions'....
if isinstance(entry, AppConfig):
app_config = entry
else:
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 = True
self.ready_event.set() #将Event的标志设置为True
目录: 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_name
self.module = app_module
self.apps = None
if not hasattr(self, 'label'):
self.label = app_name.rpartition(".")[2] #app_name=bootcamp.articles self.labe=articles
if 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 = None
self.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=ArticlesConfig
mod = import_module(mod_path) #mod_path=bootcamp.articles.apps
try:
cls = getattr(mod, cls_name) #获取到包含配置信息的类class ArticlesConfig
except AttributeError:
# ....代码省略......
if not issubclass(cls, AppConfig): #cls=class ArticlesConfig
raise ImproperlyConfigured(
"'%s' isn't a subclass of AppConfig." % entry)
try:
app_name = cls.name #获取该app文件所在目录 app_name = bootcamp.articles
except 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.articles
except 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为例)
目录:django/core/management/__init__.py
#文件 django/core/management/__init__.py
self.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 the
appropriate 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 exceptions
commands = 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_name
else:
klass = load_command_class(app_name, subcommand) #执行这里,#app_name='django.core' subcommand='migrate'
#返回一个位于django/core/management/comands/migrate.py文件中的Command类的一个实例对象
return klass
目录:django/core/management/commands/migrate.py
Command继承自BaseCommand,它本身并没有run_from_argv(self.argv)方法,而是调用父类BaseCommand的方法
同时他也没有定义初始化方法__init__()
class Command(BaseCommand):
#....部分代码省略.......
def add_arguments(self, parser):
#代码省略
@no_translations
def 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 = False
output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;"
requires_migrations_checks = False
requires_system_checks = True
base_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 = True
parser = 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 optparse
args = 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:
#代码省略......
目录:django/core/management/base.py
有一点疑问:为什么要在子类和父类之间跳来跳去呢,不懂,先跳过
class BaseCommand:
#定义了一些全局变量,看不懂,先跳过
help = ''
_called_from_command_line = False
output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;"
requires_migrations_checks = False
requires_system_checks = True
base_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.py
if 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_translations
def 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 from
db = options['database'] #db='default' 参考第11步 create_parser()
connection = connections[db] #最后,跳转到这里
#准备数据库时,后端backend所需要的Hook
connection.prepare_database() #跳过:django/db/base/base.py
#.......后续代码省略.......
目录: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 = databases
self._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.base
conn = backend.DatabaseWrapper(db, alias) #最后跳转到这一步
setattr(self._connections, alias, conn)
return conn
@cached_property
def databases(self):
if self._databases is None:
self._databases = settings.DATABASES #self._databases={ 'default': env.db('DATABASE_URL'),} bootcamp/config/settings/base.py
if 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'),}
目录: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 = None
vendor = 'unknown'
display_name = 'unknown'
SchemaEditorClass = None
# Classes instantiated in __init__().
client_class = None
creation_class = None
features_class = None
introspection_class = None
ops_class = None
validation_class = BaseDatabaseValidation
queries_limit = 9000
def __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_dict
self.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 = None
self.closed_in_transaction = False
self.errors_occurred = False
# Thread-safety related attributes.
self._thread_sharing_lock = threading.Lock()
self._thread_sharing_count = 0
self._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)
目录:django/db/__init__.py
目的:将上一步获得的数据库连接的对象conn绑定到Connections的实例对象上,可返回步骤12/13查看,由于采用了单例设计模式,系统中只有这一个实例对象,还是不是很懂,这一部分,特别是结合Threading.local()多线程局部变量,后续再回来~
--目录django/core/management/commands/migrate.py
self.migration_progress_callback是一个方法,代码将一个方法传递给了一个类,该方法也位于当前目录中
class Command(BaseCommand):
#-----部分代码省略-----------
@no_translations
def 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 not
executor = 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 = connection
self.disk_migrations = None
self.applied_migrations = None
self.ignore_no_migrations = ignore_no_migrations
if load:
self.build_graph()
--目录:django/db/migration/recorder
MigrationRecorder类主要负责将迁移的记录存储到数据库中
class MigrationRecorder:
_migration_class = None
def __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 = connection
self.disk_migrations = None
self.applied_migrations = None
self.ignore_no_migrations = ignore_no_migrations
if 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创建
目录: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_class
opts._prepare(cls) #这步是把一个autoField的类绑定到我们的Migration类对象上
#django/models/options.py
if not opts.managers: #opts.managers=None
if any(f.name == 'objects' for f in opts.fields):
raise ValueError(‘此处代码省略’)
manager = Manager() #django/db/migrations/models/manager.py !!!!!!!!!
manager.auto_created = True
cls.add_to_class('objects', manager) #objects就是在这里引入啊!!!!!!!!!!,他指向了manager类
class_prepared.send(sender=cls) #没看懂,先跳过
#文件 django/db/models/manager.py
class Manager(BaseManager.from_queryset(QuerySet)): #class Manager(BaseManagerFromQuerySet)
pass
#文件 django/db/models/manager.py
class BaseManager:
#......部分代码省略.....
@classmethod
def from_queryset(cls, queryset_class, class_name=None):
if class_name is None:
class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__) #class_name = BaseManagerFromQuerySet
return 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 = model
self._db = using
self._hints = hints or {}
self.query = query or sql.Query(self.model)
self._result_cache = None
self._sticky_filter = False
self._for_write = False
self._prefetch_related_lookups = ()
self._prefetch_done = False
self._known_related_objects = {} # {rel_field: {pk: rel_obj}}
self._iterable_class = ModelIterable
self._fields = None
def _chain(self, **kwargs):
"""
Return a copy of the current QuerySet that's ready for another
operation.
"""
obj = self._clone()
if obj._sticky_filter:
obj.query.filter_is_sticky = True
obj._sticky_filter = False
obj.__dict__.update(kwargs)
return obj
def _clone(self):
"""
Return a copy of the current QuerySet. A lightweight alternative
to deepcopy().
"""
c = self.__class__(model=self.model, query=self.query.chain(), using=self._db, hints=self._hints)
c._sticky_filter = self._sticky_filter
c._for_write = self._for_write
c._prefetch_related_lookups = self._prefetch_related_lookups[:]
c._known_related_objects = self._known_related_objects
c._iterable_class = self._iterable_class
c._fields = self._fields
return c
def using(self, alias):
"""Select which database this QuerySet should execute against."""
clone = self._chain()
clone._db = alias
return clone
目录: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()
只能到这了,搞不动。。。。。。。。。。。。。。。。。。。