vlambda博客
学习文章列表

内功修炼之django源码分析(一)

django是比较全能的python web开发框架,也是我所接触过的第一个python框架。


除了自带的后台管理页面,还支持ORM,只需要定义好对象,就可以生成数据库结构,或者由数据库结构反向生成modle,进而用python对象操作数据库。


有针对性、有目的性的阅读优秀源码,是提升编写代码质量的最好办法,这个系列的文章写了我所理解的django部分源码。


这篇文章介绍一个django程序是如何从入口runserver是加载到wsgi协议的。


初始化命令:python manage.py runserver 0:8000


#!/usr/bin/env pythonimport osimport 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_APPSexcept ImproperlyConfigured as exc: self.settings_exception = excexcept 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内部执行流程:

  1. 解析runserver后面跟的参数

  2. 加载runserver文件,

  3. 检查基础设置(INSTALLED_APPS, IP端口有没有被占用, ORM对象是否可以正常读取等

  4. 实例化WSGIserver,接收HTTP请求


最后放一段mini的runserver执行流程,也就是上面介绍的全部过程的精简版。


# 执行命令:python manange.py runserver 0:8000from django.core.management import execute_from_command_lineexecute_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 BaseCommanddef 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()