一般我们开启一个django项目,最简单的方法是进入project 目录,这时目录结构是这样的 然后我们执行python manage.py runserver,程序就开始执行了。 那django是如何从一个命令就启动整个server,启动的流程是如何的实现的呢? 首先我们来打开目录下的manage.

打开目录下的manage.py,内容是这样的:

  1. #!/usr/bin/env python
  2. import os
  3. import sys
  4.  
  5. if __name__ == "__main__":
  6. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_01.settings")
  7. from django.core.management import execute_from_command_line
  8. execute_from_command_line(sys.argv)

  

看来manage.py只是把命令行参数传给django.core.management模块中的execute_from_command_line 函数。

查看execute_from_command_line函数,可以发现实际执行的是ManagementUtility类的excute方法:

  1. def execute(self):
  2. """
  3. Given the command-line arguments, this figures out which subcommand is
  4. being run, creates a parser appropriate to that command, and runs it.
  5. """
  6. try:
  7. subcommand = self.argv[1]
  8. except IndexError:
  9. subcommand = 'help' # Display help if no arguments were given.
  10.  
  11. parser = CommandParser(None, usage="%(prog)s subcommand [options] [args]", add_help=False)
  12. parser.add_argument('--settings')
  13. parser.add_argument('--pythonpath')
  14. parser.add_argument('args', nargs='*') # catch-all
  15. try:
  16. options, args = parser.parse_known_args(self.argv[2:])
  17. handle_default_options(options)
  18. except CommandError:
  19. pass # Ignore any option errors at this point.
  20.  
  21. try:
  22. settings.INSTALLED_APPS
  23. except ImproperlyConfigured as exc:
  24. self.settings_exception = exc
  25.  
  26. if settings.configured:
  27.  
  28. if subcommand == 'runserver' and '--noreload' not in self.argv:
  29. try:
  30. autoreload.check_errors(django.setup)()
  31. except Exception:
  32.  
  33. apps.all_models = defaultdict(OrderedDict)
  34. apps.app_configs = OrderedDict()
  35. apps.apps_ready = apps.models_ready = apps.ready = True
  36.  
  37. _parser = self.fetch_command('runserver').create_parser('django', 'runserver')
  38. _options, _args = _parser.parse_known_args(self.argv[2:])
  39. for _arg in _args:
  40. self.argv.remove(_arg)
  41.  
  42. # In all other cases, django.setup() is required to succeed.
  43. else:
  44. django.setup()
  45.  
  46. self.autocomplete()
  47.  
  48. if subcommand == 'help':
  49. if '--commands' in args:
  50. sys.stdout.write(self.main_help_text(commands_only=True) + '\n')
  51. elif len(options.args) < 1:
  52. sys.stdout.write(self.main_help_text() + '\n')
  53. else:
  54. self.fetch_command(options.args[0]).print_help(self.prog_name, options.args[0])
  55.  
  56. elif subcommand == 'version' or self.argv[1:] == ['--version']:
  57. sys.stdout.write(django.get_version() + '\n')
  58. elif self.argv[1:] in (['--help'], ['-h']):
  59. sys.stdout.write(self.main_help_text() + '\n')
  60. else:
  61. self.fetch_command(subcommand).run_from_argv(self.argv)

  其中的

  1. parser = CommandParser(None, usage="%(prog)s subcommand [options] [args]", add_help=False)
  2. parser.add_argument('--settings')
  3. parser.add_argument('--pythonpath')
  4. parser.add_argument('args', nargs='*') # catch-all
  5. try:
  6. options, args = parser.parse_known_args(self.argv[2:])
  7. handle_default_options(options)
  8. except CommandError:
  9. pass # Ignore any option errors at this point.

  CommandParser其实类似于Argparse的一个解析命令行参数的类,从代码里可以看出我们可以直接在命令行指定settings文件和pythonpath。

  1. no_settings_commands = [
  2. 'help', 'version', '--help', '--version', '-h',
  3. 'compilemessages', 'makemessages',
  4. 'startapp', 'startproject',
  5. ]
  6. try:
  7. settings.INSTALLED_APPS
  8. except ImproperlyConfigured as exc:
  9. self.settings_exception = exc
  10.  
  11. if subcommand in no_settings_commands:
  12. settings.configure()

  这块代码就可以解释我们执行python manage.py start project 时django在背后会调用settings.configure方法,这里的settings是指django.conf.LazySettings的一个实例,configure方法其实就是使用django.conf.global_settings.py中的默认设置创建一份新的配置文件,作为我们新创建的project的settings.py

  1. if settings.configured:
  2.  
  3. if subcommand == 'runserver' and '--noreload' not in self.argv:
  4. try:
  5. autoreload.check_errors(django.setup)()
  6. except Exception:
  7.  
  8. apps.all_models = defaultdict(OrderedDict)
  9. apps.app_configs = OrderedDict()
  10. apps.apps_ready = apps.models_ready = apps.ready = True
  11.  
  12. # In all other cases, django.setup() is required to succeed.
  13. else:
  14. django.setup()

  autoreload.check_errors(django.setup)()其实也是调用django.setup方法,而django.setup方法

  1. def setup():
  2. """
  3. Configure the settings (this happens as a side effect of accessing the
  4. first setting), configure logging and populate the app registry.
  5. """
  6. from django.apps import apps
  7. from django.conf import settings
  8. from django.utils.log import configure_logging
  9.  
  10. configure_logging(settings.LOGGING_CONFIG, settings.LOGGING)
  11. apps.populate(settings.INSTALLED_APPS)

  

负责初始化日志模块以及所有应用.

抽丝剥茧

剩下的代码最重要的就是这一句:

  1. self.fetch_command(subcommand).run_from_argv(self.argv)

  fetch_command会根据subcommand(这是我们执行python manage.py rumserver时传入的第二个参数:runserver),去django.core.management.commands中查找对应的command类,然后把所有命令行参数传给run_from_argv方法并执行,在runserver这个示例中,最终会调用django.utils.autoreload中的python_reloader或者jython_reloader新开一个线程:

  1. def python_reloader(main_func, args, kwargs):
  2. if os.environ.get("RUN_MAIN") == "true":
  3. thread.start_new_thread(main_func, args, kwargs)
  4. try:
  5. reloader_thread()
  6. except KeyboardInterrupt:
  7. pass
  8. else:
  9. try:
  10. exit_code = restart_with_reloader()
  11. if exit_code < 0:
  12. os.kill(os.getpid(), -exit_code)
  13. else:
  14. sys.exit(exit_code)
  15. except KeyboardInterrupt:
  16. pass

  这里的main_func是commands/runserver.py中的inner_run方法:

  1. def inner_run(self, *args, **options):
  2. # If an exception was silenced in ManagementUtility.execute in order
  3. # to be raised in the child process, raise it now.
  4. autoreload.raise_last_exception()
  5.  
  6. threading = options.get('use_threading')
  7. shutdown_message = options.get('shutdown_message', '')
  8. quit_command = 'CTRL-BREAK' if sys.platform == 'win32' else 'CONTROL-C'
  9.  
  10. self.stdout.write("Performing system checks...\n\n")
  11. self.check(display_num_errors=True)
  12. self.check_migrations()
  13. now = datetime.now().strftime('%B %d, %Y - %X')
  14. if six.PY2:
  15. now = now.decode(get_system_encoding())
  16. self.stdout.write(now)
  17. self.stdout.write((
  18. "Django version %(version)s, using settings %(settings)r\n"
  19. "Starting development server at http://%(addr)s:%(port)s/\n"
  20. "Quit the server with %(quit_command)s.\n"
  21. ) % {
  22. "version": self.get_version(),
  23. "settings": settings.SETTINGS_MODULE,
  24. "addr": '[%s]' % self.addr if self._raw_ipv6 else self.addr,
  25. "port": self.port,
  26. "quit_command": quit_command,
  27. })
  28.  
  29. try:
  30. handler = self.get_handler(*args, **options)
  31. run(self.addr, int(self.port), handler,
  32. ipv6=self.use_ipv6, threading=threading)
  33. except socket.error as e:
  34. # Use helpful error messages instead of ugly tracebacks.
  35. ERRORS = {
  36. errno.EACCES: "You don't have permission to access that port.",
  37. errno.EADDRINUSE: "That port is already in use.",
  38. errno.EADDRNOTAVAIL: "That IP address can't be assigned to.",
  39. }
  40. try:
  41. error_text = ERRORS[e.errno]
  42. except KeyError:
  43. error_text = force_text(e)
  44. self.stderr.write("Error: %s" % error_text)
  45. # Need to use an OS exit because sys.exit doesn't work in a thread
  46. os._exit(1)
  47. except KeyboardInterrupt:
  48. if shutdown_message:
  49. self.stdout.write(shutdown_message)
  50. sys.exit(0)

  最关键的是这两条语句:

  1. handler = self.get_handler(*args, **options)
  2. run(self.addr, int(self.port), handler,ipv6=self.use_ipv6, threading=threading)

  

get_handler会返回django.core.servers.basehttp中定义的一个application(其实就是我们project下的wigs.py中定义的application)

这是run函数的内容

  1. def run(addr, port, wsgi_handler, ipv6=False, threading=False):
  2. server_address = (addr, port)
  3. if threading:
  4. httpd_cls = type(str('WSGIServer'), (socketserver.ThreadingMixIn, WSGIServer), {})
  5. else:
  6. httpd_cls = WSGIServer
  7. httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
  8. if threading:
  9. httpd.daemon_threads = True
  10. httpd.set_app(wsgi_handler)
  11. httpd.serve_forever()

  可以看出run函数其实就是启动一个WSGIServer实例(WSGIServer继承python内置类simple_server.WSGIServer),并把handler设置为前面get_handler的返回值

Django源码分析之程序执行入口分析的更多相关文章

  1. Django源码分析之启动wsgi发生的事

    前言 ​ 好多人对技术的理解都停留在懂得使用即可,因而只会用而不会灵活用,俗话说好奇害死猫,不然我也不会在凌晨1.48的时候决定写这篇博客,好吧不啰嗦了 ​ 继续上一篇文章,后我有个问题(上文:&qu ...

  2. 2、Django源码分析之启动wsgi发生了哪些事

    一 前言 Django是如何通过网络socket层接收数据并将请求转发给Django的urls层? 有的人张口就来:就是通过wsgi(Web Server Gateway Interface)啊! D ...

  3. Django 源码小剖: 应用程序入口 WSGIHandler

    WSGI 有三个部分, 分别为服务器(server), 应用程序(application) 和中间件(middleware). 已经知道, 服务器方面会调用应用程序来处理请求, 在应用程序中有真正的处 ...

  4. django源码分析 python manage.py runserver

    django是一个快速开发web应用的框架, 笔者也在django框架上开发不少web应用,闲来无事,就想探究一下django底层到底是如何实现的,本文记录了笔者对django源码的分析过程 I be ...

  5. spring源码系列(十): 读取xml入口类 ClassPathXmlApplicationContext 分析

    环境准备: 使用spring5.1.6版本 1 xml配置文件 <?xml version="1.0" encoding="UTF-8"?> < ...

  6. Django 源码小剖: 初探 WSGI

    Django 源码小剖: 初探 WSGI python 作为一种脚本语言, 已经逐渐大量用于 web 后台开发中, 而基于 python 的 web 应用程序框架也越来越多, Bottle, Djan ...

  7. Django 源码小剖: 响应数据 response 的返回

    响应数据的返回 在 WSGIHandler.__call__(self, environ, start_response) 方法调用了 WSGIHandler.get_response() 方法, 由 ...

  8. Django 源码小剖: 初探中间件(middleware)

    因为考虑到文章的长度, 所以 BaseHandler 的展开被推迟了. 在 BaseHandler 中隐藏着中间件的信息, 较常见的 SessionMiddleware 就已经默认安装.  BaseH ...

  9. django源码阅读

    最近再看django-bootstrap-toolkit,一直困惑于静态文件的路径问题.所以只能从源码入手了.   从manage.py开始.manage.py 比较简单就几句话. #!/usr/bi ...

随机推荐

  1. websocket简介

    在我们做web项目的过程中,经常需要做的一个模块是消息模块.典型的场景:一个商城系统的后台管理,我想实现如果前台有客户下单,后台就会接到消息,以便尽快发货处理. 要实现上述的功能,我们有几种备选的方案 ...

  2. 【功能代码】---3 JS判断字符串是否包含某个字符串

    JS判断字符串是否包含某个字符串 var str ="abc"; if(str.indexOf("bc")>-1){ alert('str中包含bc字符串 ...

  3. iOS手势冲突问题

    今天在做一个效果的时候,由于子视图和父视图都有响应的事件,子视图的事件理所当然被父视图拦截掉了,接下来就做分析解决 1.  tableviewcell可以触发点击,同时tableview的父视图有点击 ...

  4. 微信小程序教学第四章第一节(含视频):小程序中级实战教程:详情-页面制作

    详情 - 页面制作 本文配套视频地址: https://v.qq.com/x/page/o0555o20xjd.html 开始前请把 ch4-1 分支中的 code/ 目录导入微信开发工具 这一章节中 ...

  5. 封装一个button上带图片的,图片在上,文字在下的按钮

    #import "CJShoppingDetailButton.h" @implementation CJShoppingDetailButton - (void)layoutSu ...

  6. ArcGIS 网络分析[8.1] 资料1 使用AO打开或创建网络数据集之【打开】

    为了创建或打开一个网络数据集,你必须使用NetworkDatasetFDExtension对象(文件地理数据库中的数据集)或NetworkDatasetWorkspaceExtension对象(对于S ...

  7. lesson - 5 Linux用户和组管理

    1. /etc/passwd由 : 分隔成7个字段(1) 用户名 规则:大小写字母.数字.减号(不能出现在首位).点以及下划线,其他字符不合法 (2) x 放密码,安全起见放到 /etc/shadow ...

  8. Python的range函数详细用法

    1. >>> range(1,5)  #代表从1到5(不包含5) [1, 2, 3, 4]>>> 2. >>> range(1,5,2) #代表从 ...

  9. maven jar包冲三种解决方式

    初次启动应用,一直包如下错误,起初怀疑引入pandora 版本冲突. Exception in thread "main" java.lang.NoSuchMethodError: ...

  10. shell脚本-批量执行机器命令

    场景:通过跳板机,批量获取线上机器日志 使用方式:run2 host 'ls -al /home/admin/' #! /bin/sh USER_NAME=$USER if [ $# -ne 2 ]; ...