内功修炼之django源码分析(一)
django是比较全能的python web开发框架,也是我所接触过的第一个python框架。
除了自带的后台管理页面,还支持ORM,只需要定义好对象,就可以生成数据库结构,或者由数据库结构反向生成modle,进而用python对象操作数据库。
有针对性、有目的性的阅读优秀源码,是提升编写代码质量的最好办法,这个系列的文章写了我所理解的django部分源码。
这篇文章介绍一个django程序是如何从入口runserver是加载到wsgi协议的。
初始化命令:python manage.py runserver 0:8000
#!/usr/bin/env python
import os
import sys
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'smartDR.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
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?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
由第20行进入进入,执行函数main。
第8行,加载settings.py这个django默认的配置文件。
第10行,导入 execute_from_command_line函数。
第17行,执行上面函数。进入execute_from_command_line
def execute_from_command_line(argv=None):
"""Run a ManagementUtility."""
utility = ManagementUtility(argv)
utility.execute()
这里实例化了一个类ManagementUtility(argv),并且调用execut()方法。
进入ManagementUtility这个类。
def __init__(self, argv=None):
self.argv = argv or sys.argv[:]
self.prog_name = os.path.basename(self.argv[0])
if self.prog_name == '__main__.py':
self.prog_name = 'python -m django'
self.settings_exception = None
在初始化函数中设置了argv参数(第二行)。
因为我们只关心执行的逻辑,所以直接跳到被调用的excute()。
try:
subcommand = self.argv[1]
except IndexError:
subcommand = 'help' # Display help if no arguments were given.
try:
settings.INSTALLED_APPS
except ImproperlyConfigured as exc:
self.settings_exception = exc
except ImportError as exc:
self.settings_exception = exc
在这里先定义了subcommand,就是输入的参数'runserver'。
然后引入了配置文件settings.py中的INSTALLED_APPS。
if settings.configured:
if subcommand == 'runserver' and '--noreload' not in self.argv:
try:
autoreload.check_errors(django.setup)()
except Exception:
apps.all_models = defaultdict(dict)
apps.app_configs = {}
apps.apps_ready = apps.models_ready = apps.ready = True
_parser = self.fetch_command('runserver').create_parser('django', 'runserver')
_options, _args = _parser.parse_known_args(self.argv[2:])
for _arg in _args:
self.argv.remove(_arg)
第一行检测是否有settings配置,如果没有是无法向下执行的。
第二行确认需要执行的命令是不是runserver,如果是进入错误检测阶段,否则进行配置。
if subcommand == 'help':
if '--commands' in args:
sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
elif not options.args:
sys.stdout.write(self.main_help_text() + '\n')
else:
self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
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)
这里根据不同的参数会有不同的效果。如果不是查看帮助或者版本信息则进入self.fetch_command(subcommand).run_from_argv(self.argv)
(如果有处理参数的场景可以参考一下这段代码,很优雅的参数输入处理方式)
def fetch_command(self, subcommand):
commands = get_commands()
app_name = commands[subcommand]
if isinstance(app_name, BaseCommand):
klass = app_name
else:
klass = load_command_class(app_name, subcommand)
这个方法传了一个参数subcommand,第二行获取到所有方法的信息,字典类型
第三行获取该方法的name,isinstance(app_name, BaseCommand)判断runserver这个指令是否已经被加载了,比如启动的情况下修改了文件。
如果没有被加载进入load_command_class(app_name, subcommand) 这个方法
def load_command_class(app_name, name):
module = import_module('%s.management.commands.%s' % (app_name, name))
return module.Command()
这个方法的功能就是根据两个参数加载对应的文件。
这里直接使用了import_module,这个方法如果点进去可以看到是自定义的导特定路径的模块,放在这里就是导入了特定路径的runserver.py的这个文件,返回值中又调用了Command()这个方法。
我们直接找到runserver.py中的这个方法。
class Command(RunserverCommand):
help = "Starts a lightweight Web server for development and also serves static files."
def add_arguments(self, parser):...
def get_handler(self, *args, **options):...
这个方法中并没有我们上面的run_from_argv这个方法,所以再搜索它的父类RunserverCommand。
其实父类RunserverCommand中也没有run_from_argv这个方法,所以我们继续搜索父类BaseCommand。
def run_from_argv(self, argv):
self.execute(*args, **cmd_options)
这里面调用了excute()。那么这个excute()是谁执行的呢,其实是上一层的runserver.py。
def execute(self, *args, **options):
if options['no_color']:
os.environ["DJANGO_COLORS"] = "nocolor"
super().execute(*args, **options)
在这里又调用了父类的excute()。(反复横跳?)再看父类的excute()。可以发现这里是设置了标准输出output。(此处省略了父类的excute的代码)
到了这里就能看到输出的runserver的运行结果啦。但是整个过程还没有结束,最后还会调用一个第一个runserver.py中的get_handler()。
def get_handler(self, *args, **options):
handler = super().get_handler(*args, **options)
return handler
def get_handler(self, *args, **options):
return get_internal_wsgi_application()
他调用了她父类的get_handler()。最终作用就是执get_internal_wsgi_application(),用来获取wsgi的get_internal_wsgi_application()。
至此runserver这个命令的流程就完全结束了。下面总结一下。
django内部执行流程:
解析runserver后面跟的参数
加载runserver文件,
检查基础设置(INSTALLED_APPS, IP端口有没有被占用, ORM对象是否可以正常读取等)
实例化WSGIserver,接收HTTP请求
最后放一段mini的runserver执行流程,也就是上面介绍的全部过程的精简版。
# 执行命令:python manange.py runserver 0:8000
from django.core.management import execute_from_command_line
execute_from_command_line(('manage.py', 'runserver', '0:8000'))
def execute_from_command_line(argv=('manage.py', 'runserver', '0:8000')):
utility = ManagementUtility(('manage.py', 'runserver', '0:8000'))
utility.execute()
def __init__(self, argv=('manage.py', 'runserver', '0:8000')):
self.argv = ('manage.py', 'runserver', '0:8000')
subcommand = self.argv[1] # runserver
settings.INSTALLED_APPS
if settings.configured:
if subcommand == 'runserver' and '--noreload' not in self.argv:
try:
autoreload.check_errors(django.setup)()
self.fetch_command(subcommand).run_from_argv(self.argv) # 获取runserver然后执行
def fetch_command(self, subcommand):
commands = get_commands()
app_name = commands[subcommand]
if isinstance(app_name, BaseCommand):
klass = app_name
else:
klass = load_command_class(app_name, subcommand) # 加载runserver这个类
def load_command_class(app_name, name):
module = import_module('%s.management.commands.%s' % (app_name, name)) # 导入runserver
return module.Command()
# 上面的这个方法导入的runserver.py在 site-packahes/django/contrib/staticfiles/management/commands/runserver.py调用的是这个文件的Command类,也是上文中的"第一个runserver.py"
# class Command(RunserverCommand) # 则这里没有找到run_from_argv,进入父类
# 他的父类是 django/core/management/commands/runserver.py,这里也没有run_from_argv,继续搜索他的父类
# 位置在django/core/management/base.py
# class BaseCommand
def run_from_argv(self, argv):
self.execute(*args, **cmd_options)
def get_handler(self, *args, **options):
handler = super().get_handler(*args, **options)
return handler
def get_handler(self, *args, **options):
return get_internal_wsgi_application()