记录一下django的启动过程,通常在开发阶段使用./manage.py subcommand options启动项目。
框架的处理方法主要集中在django包中,我们看看该包中都含有写什么文件

简单介绍一下每个文件的用途:
Apps,处理app载入等问题,存储已经载入的所有app
Bin,django-admin文件,框架启动的入口之一
Conf,主要有两个作用:1) 处理全局配置, 比如数据库、加载的应用、MiddleWare等 2) 处理urls配置, 就是url与View的映射关系。
Contrib,由Django的开发者贡献的功能模块,不过既然都已经随版本发布,就表示是官方的。
Core,Django的核心处理库,包括url分析、处理请求、缓存等,其中处理请求是核心了,比如处理fastcgi就是由wsgi.py处理。
Db,顾名思义,处理与数据库相关的,就是ORM。
Dispatch,其实这不是Django原创,是pydispatch库,主要处理消费者-工作者模式。
Forms,处理html的表单,不用多介绍。
Http,request、response、cookie等处理
Middleware,中间件,就是处理HTTP的request和response的,类似插件。比如默认的common中间件的一个功能:当一个页面没有找对对应的pattern时,会自定加上?/?重新处理。比如访问/blog时,而定义的pattern是?^blog/$?,所以找不到对应的pattern,会自动再用/blog/查找,当然前提是APPEND_SLASH=True。
Template,Django的模板。
Templatetags,处理Application的tag的wrapper,就是将INSTALLED_APPS中所有的Templatetags目录添加到Django.Templatetags目录中,则当使用{{load blog}}记载tag时,就可以使用import Django.Templatetags.blog 方式加载了。不过这有一个问题,如果其他Application目录中也有blog.py,这会加载第一个出现blog.py的tag。其实在Django中,有许多需要处理重名的地方,比如Template,需要格外小心,这个后续在介绍。
Test,测试相关
Urls,url处理
Utils,公共库,很多公用的类都在放在这里。
Views,最基本的View方法。
接下来看看execute_from_command_line函数是如何做的:
本文以用户键入./manage.py runserver 0.0.0.0:8080为例,autoreload.check_errors(django.setup)()将django.setup命令运行是否正确放入检查之中
self.autocomplete()做一些参数和系统配置以及操作系统匹配等工作。
self.fetch_command(subcommand).run_from_argv(self.argv)为重点,是用户输入的命令的真实入口。
根据该函数可以看出来,所有命令处理文件均放在django/core/management/commands下面,django适用的命名有:

runserver.Command()中没有run_from_argv方法,但是其继承了BaseCommand,所以我们查看BaseCommand().run_from_argv方法
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_demo.settings")
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django
except ImportError:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
)
raise
execute_from_command_line(sys.argv)
查看manage.py文件,首先将本项目的settings文件位置放入系统变量之中,然后启动django.core.management中的execute_from_command_line方法,正式进入Django框架处理的过程。框架的处理方法主要集中在django包中,我们看看该包中都含有写什么文件
简单介绍一下每个文件的用途:
Apps,处理app载入等问题,存储已经载入的所有app
Bin,django-admin文件,框架启动的入口之一
Conf,主要有两个作用:1) 处理全局配置, 比如数据库、加载的应用、MiddleWare等 2) 处理urls配置, 就是url与View的映射关系。
Contrib,由Django的开发者贡献的功能模块,不过既然都已经随版本发布,就表示是官方的。
Core,Django的核心处理库,包括url分析、处理请求、缓存等,其中处理请求是核心了,比如处理fastcgi就是由wsgi.py处理。
Db,顾名思义,处理与数据库相关的,就是ORM。
Dispatch,其实这不是Django原创,是pydispatch库,主要处理消费者-工作者模式。
Forms,处理html的表单,不用多介绍。
Http,request、response、cookie等处理
Middleware,中间件,就是处理HTTP的request和response的,类似插件。比如默认的common中间件的一个功能:当一个页面没有找对对应的pattern时,会自定加上?/?重新处理。比如访问/blog时,而定义的pattern是?^blog/$?,所以找不到对应的pattern,会自动再用/blog/查找,当然前提是APPEND_SLASH=True。
Template,Django的模板。
Templatetags,处理Application的tag的wrapper,就是将INSTALLED_APPS中所有的Templatetags目录添加到Django.Templatetags目录中,则当使用{{load blog}}记载tag时,就可以使用import Django.Templatetags.blog 方式加载了。不过这有一个问题,如果其他Application目录中也有blog.py,这会加载第一个出现blog.py的tag。其实在Django中,有许多需要处理重名的地方,比如Template,需要格外小心,这个后续在介绍。
Test,测试相关
Urls,url处理
Utils,公共库,很多公用的类都在放在这里。
Views,最基本的View方法。
接下来看看execute_from_command_line函数是如何做的:
def execute_from_command_line(argv=None):
"""
A simple method that runs a ManagementUtility.
"""
utility = ManagementUtility(argv)
utility.execute()
ManagementUtility初始化的时候将系统参数初始化,然后执行execute方法def execute(self):
"""
Given the command-line arguments, this figures out which subcommand is
being run, creates a parser appropriate to that command, and runs it.
"""
try:
subcommand = self.argv[1]
except IndexError:
subcommand = 'help' # Display help if no arguments were given.
# Preprocess options to extract --settings and --pythonpath.
# These options could affect the commands that are available, so they
# must be processed early.
parser = CommandParser(None, usage="%(prog)s subcommand [options] [args]", add_help=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:])
handle_default_options(options)
except CommandError:
pass # Ignore any option errors at this point.
no_settings_commands = [
'help', 'version', '--help', '--version', '-h',
'compilemessages', 'makemessages',
'startapp', 'startproject',
]
try:
settings.INSTALLED_APPS
except ImproperlyConfigured as exc:
self.settings_exception = exc
# A handful of built-in management commands work without settings.
# Load the default settings -- where INSTALLED_APPS is empty.
if subcommand in no_settings_commands:
settings.configure()
if settings.configured:
# Start the auto-reloading dev server even if the code is broken.
# The hardcoded condition is a code smell but we can't rely on a
# flag on the command class because we haven't located it yet.
if subcommand == 'runserver' and '--noreload' not in self.argv:
try:
autoreload.check_errors(django.setup)()
except Exception:
# The exception will be raised later in the child process
# started by the autoreloader. Pretend it didn't happen by
# loading an empty list of applications.
apps.all_models = defaultdict(OrderedDict)
apps.app_configs = OrderedDict()
apps.apps_ready = apps.models_ready = apps.ready = True
# In all other cases, django.setup() is required to succeed.
else:
django.setup()
self.autocomplete()
if subcommand == 'help':
if '--commands' in args:
sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
elif len(options.args) < 1:
sys.stdout.write(self.main_help_text() + '\n')
else:
self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
# Special-cases: We want 'django-admin --version' and
# 'django-admin --help' to work, for backwards compatibility.
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)
subcommand用来存储用户输入的命令;parser用来检查命令附带的参数;handle_default_options方法用来载入settingsdef handle_default_options(options):
"""
Include any default options that all commands should accept here
so that ManagementUtility can handle them before searching for
user commands.
"""
if options.settings:
os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
if options.pythonpath:
sys.path.insert(0, options.pythonpath)
settings.INSTALLED_APPS用来载入用户配置文件中配置的所有app,根据from django.conf import settings查看得知settings = LazySettings(),该类具有__getattr__方法,所以这种用法相当于调用LazySettings().__getattr__(INSTALLED_APPS)。最终实现的功能是读取用户提供的配置文件,以及框架目录中的global_settings文件,所有配置项如果用户有配置则载入用户配置的内容,如果没有则载入框架提供的配置文件中的内容。本文以用户键入./manage.py runserver 0.0.0.0:8080为例,autoreload.check_errors(django.setup)()将django.setup命令运行是否正确放入检查之中
def setup(set_prefix=True):
"""
Configure the settings (this happens as a side effect of accessing the
first setting), configure logging and populate the app registry.
Set the thread-local urlresolvers script prefix if `set_prefix` is True.
"""
from django.apps import apps
from django.conf import settings
from django.urls import set_script_prefix
from django.utils.encoding import force_text
from django.utils.log import configure_logging
configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
if set_prefix:
set_script_prefix(
'/' if settings.FORCE_SCRIPT_NAME is None else force_text(settings.FORCE_SCRIPT_NAME)
)
apps.populate(settings.INSTALLED_APPS)
配置log相关信息,设置当前目录前缀,apps = Apps(installed_apps=None),Apps初始化的时候定义了全局的多个字典、变量、线程锁等。populate方法循环载入之前从配置文件中收录的应用和模块,并初始化这个线程安全的对象。self.autocomplete()做一些参数和系统配置以及操作系统匹配等工作。
self.fetch_command(subcommand).run_from_argv(self.argv)为重点,是用户输入的命令的真实入口。
def fetch_command(self, subcommand):
"""
Tries 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()
try:
app_name = commands[subcommand]
except KeyError:
if os.environ.get('DJANGO_SETTINGS_MODULE'):
# If `subcommand` is missing due to misconfigured settings, the
# following line will retrigger an ImproperlyConfigured exception
# (get_commands() swallows the original one) so the user is
# informed about it.
settings.INSTALLED_APPS
else:
sys.stderr.write("No Django settings specified.\n")
sys.stderr.write(
"Unknown command: %r\nType '%s help' for usage.\n"
% (subcommand, self.prog_name)
)
sys.exit(1)
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)
return klass
其中fetch_command根据subcommand返回对应的命令处理文件,load_command_class函数返回module.Command(),在本例中就是self.fetch_command(subcommand)就是runserver.Command(),这是一个多态的实现。根据该函数可以看出来,所有命令处理文件均放在django/core/management/commands下面,django适用的命名有:
runserver.Command()中没有run_from_argv方法,但是其继承了BaseCommand,所以我们查看BaseCommand().run_from_argv方法
def run_from_argv(self, argv):
"""
Set up any environment changes requested (e.g., Python path
and Django settings), then run this command. If the
command raises a ``CommandError``, intercept it and print it sensibly
to stderr. If the ``--traceback`` option is present or the raised
``Exception`` is not ``CommandError``, raise it.
"""
self._called_from_command_line = True
parser = self.create_parser(argv[0], argv[1])
options = parser.parse_args(argv[2:])
cmd_options = vars(options)
# Move positional args out of options to mimic legacy optparse
args = cmd_options.pop('args', ())
handle_default_options(options)
try:
self.execute(*args, **cmd_options)
except Exception as e:
if options.traceback or not isinstance(e, CommandError):
raise
# SystemCheckError takes care of its own formatting.
if isinstance(e, SystemCheckError):
self.stderr.write(str(e), lambda x: x)
else:
self.stderr.write('%s: %s' % (e.__class__.__name__, e))
sys.exit(1)
finally:
connections.close_all()
其中调用了execute方法,然后将连接关闭,此处为数据库连接,可从execute方法中看出程序进行了数据库连接操作。def execute(self, *args, **options):
"""
Try to execute this command, performing system checks if needed (as
controlled by the ``requires_system_checks`` attribute, except if
force-skipped).
"""
if options['no_color']:
self.style = no_style()
self.stderr.style_func = None
if options.get('stdout'):
self.stdout = OutputWrapper(options['stdout'])
if options.get('stderr'):
self.stderr = OutputWrapper(options['stderr'], self.stderr.style_func)
saved_locale = None
if not self.leave_locale_alone:
# Only mess with locales if we can assume we have a working
# settings file, because django.utils.translation requires settings
# (The final saying about whether the i18n machinery is active will be
# found in the value of the USE_I18N setting)
if not self.can_import_settings:
raise CommandError("Incompatible values of 'leave_locale_alone' "
"(%s) and 'can_import_settings' (%s) command "
"options." % (self.leave_locale_alone,
self.can_import_settings))
# Deactivate translations, because django-admin creates database
# content like permissions, and those shouldn't contain any
# translations.
from django.utils import translation
saved_locale = translation.get_language()
translation.deactivate_all()
try:
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)
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)
finally:
if saved_locale is not None:
translation.activate(saved_locale)
return output
self.handle方法对应的是runserver.Command()中的该方法,也就是runserver命令具体的工作内容了,该部分执行完成之后进行数据库连接。runserver具体执行稍后总结。至此所有django提供的命令均可以找到各自的入口了。