运行django项目,我们除了可以通过django图形界面启动,我们也可以通过命令行方式启动,启动方式如下:

python manage.py runserver

当我们创建django项目时候,会生成如下目录

mysite/
├── manage.py  # 管理文件
└── mysite  # 项目目录├── __init__.py├── settings.py  # 配置├── urls.py  # 路由 --> URL和函数的对应关系└── wsgi.py  # runserver命令就使用wsgiref模块做简单的web server

我们来看下python manage.py runserver中的manage.py做了什么操作,可以用来启动django项目

import os
import sysif __name__ == "__main__":os.environ.setdefault("DJANGO_SETTINGS_MODULE", "s8day88.settings")try:from django.core.management import execute_from_command_lineexcept ImportError:try:import djangoexcept 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?")raiseexecute_from_command_line(sys.argv)

上述代码中setdefault是不存在添加一个键值对,存在则返回该键对应的值
然后执行try内部代码from django.core.management import execute_from_command_line
导入management模块,最后执行该模块下的如下方法

execute_from_command_line(sys.argv)

我们来看下上述一行代码中的参数sys.argv是什么东东?经过测试输出如下的结果

['E:/python/django_pro/manage.py', 'runserver', '8000']

参数意思就是manage.py的绝对路径,命令,端口
接下来我们看下方法execute_from_command_line(sys.argv)方法吧

def execute_from_command_line(argv=None):utility = ManagementUtility(argv)utility.execute()

上面代码实例了一个类ManagementUtility对象,然后调用了一个对象方法execute
在构造对象时,在__init__方法中初始化如下代码

    def __init__(self, argv=None):self.argv = argv or sys.argv[:]self.prog_name = os.path.basename(self.argv[0])self.settings_exception = None

赋值self.argv=argvself.prog_name="manage.py"路径,接下来我们看下execute方法吧!

代码有些长,我先把代码贴出来,然后在一点点看

    def execute(self):try:subcommand = self.argv[1]except IndexError:subcommand = 'help'  # Display help if no arguments were given.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-alltry:options, args = parser.parse_known_args(self.argv[2:])handle_default_options(options)except CommandError:pass  # Ignore any option errors at this point.try:settings.INSTALLED_APPSexcept ImproperlyConfigured as exc:self.settings_exception = excif settings.configured:if subcommand == 'runserver' and '--noreload' not in self.argv:try:autoreload.check_errors(django.setup)()except Exception:apps.all_models = defaultdict(OrderedDict)apps.app_configs = OrderedDict()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)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])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)

首先parser实例化一个CommandParser类对象解析器,并且add_argument添加如下的代码块

 parser.add_argument('--settings')parser.add_argument('--pythonpath')parser.add_argument('args', nargs='*')  # catch-all

我们具体也不知道到底是如何一步步解析的,也没有太大的必要去看CommandParser类代码
我们继续看代码进入try语句,我么看到有如下一行代码

options, args = parser.parse_known_args(self.argv[2:])
handle_default_options(options)

我们将options, args打印出来,看下输出结果

Namespace(args=['8000'], pythonpath=None, settings=None) [] @@@@@@@@@@@@@@@

看到上述打印,我们大概猜出parser对象,就是获取传递过来的['E:/python/django_pro/manage.py', 'runserver', '8000']参数,然后自己在封装add_argument需要的数据格式

接下来我们继续看代码,接下来继续执行如下代码

 settings.INSTALLED_APPS

这里就熟悉了,这不是我们settings.py文件中的如下配置么,是我们熟悉的admin模块、用户认真auth模块、sessions模块等,获取了一个字符串的列表数据

INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01.apps.App01Config',"rbac.apps.RbacConfig"
]

我们知道subcommand = self.argv[1]截取的字符串是runserver
接下来继续执行代码subcommand == 'runserver' and '--noreload' not in self.argv:
继而进入try语句代码autoreload.check_errors(django.setup)()
我们来看下check_errors函数做了什么操作处理?

def check_errors(fn):def wrapper(*args, **kwargs):global _exceptiontry:fn(*args, **kwargs)except Exception:_exception = sys.exc_info()et, ev, tb = _exceptionif getattr(ev, 'filename', None) is None:# get the filename from the last item in the stackfilename = traceback.extract_tb(tb)[-1][0]else:filename = ev.filenameif filename not in _error_files:_error_files.append(filename)raisereturn wrapper

哦原来check_errors是一个装饰器函数,该方法接受一个参数,该参数类型是一个函数django.setup,然后返回一个函数wrapperautoreload.check_errors(django.setup)()代码意思就是最后执行了装饰器函数中的fn(*args, **kwargs),也就是传递的参数django.setup,好我们稍后django.setup做了什么操作?

基本上execute函数也快执行完毕,我们看下最后的代码是做了什么收尾操作?
紧接着执行代码 self.autocomplete()如下

def autocomplete(self):
、、、、
、、、、
、、、、
sys.exit(0)

大概意思就是sys模块使用完毕,去退出该模块,从而做一些sys模块的功能,这里就不去细看了
接下来继续执行execute方法,也就是execute方法的最后一行代码

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

上一行代码,我们通过代码run_from_argv字面含义,大概能猜到应该是需要某个模块类对象,然后启动某些东西

ok我们在分析execute方法时候,autoreload.check_errors(django.setup)()接下来我们就回过头来,去分析它,看看它里面是如何进行加载配置的
如下是django.setup代码部分

from __future__ import unicode_literalsfrom django.utils.version import get_versionVERSION = (1, 11, 9, 'final', 0)__version__ = get_version(VERSION)
def setup(set_prefix=True):from django.apps import appsfrom django.conf import settingsfrom django.urls import set_script_prefixfrom django.utils.encoding import force_textfrom django.utils.log import configure_loggingconfigure_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)

代码中configure_logging大概是进行log配置,set_script_prefix以及设置一些前缀补充完整,这两个方法直接过,我们看如下有价值的代码,是调用了模块from django.apps import apps的类方法populate

apps.populate(settings.INSTALLED_APPS)

我也是先贴出populate代码,然后在逐行分析

    def populate(self, installed_apps=None):if self.ready:returnwith self._lock:if self.ready:returnif self.app_configs:raise RuntimeError("populate() isn't reentrant")for entry in installed_apps:if isinstance(entry, AppConfig):app_config = entryelse:app_config = AppConfig.create(entry)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_configapp_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

代码中通过with函数开启了self._lock = threading.Lock()锁,然后循环installed_apps,如果是AppConfig实例类型就返回,否则就去加载app和实例化该app的类对象app_config = AppConfig.create(entry)

 @classmethoddef create(cls, entry):try:module = import_module(entry)except ImportError:module = Nonemod_path, _, cls_name = entry.rpartition('.')if not mod_path:raiseelse:try:entry = module.default_app_configexcept AttributeError:# Otherwise, it simply uses the default app config class.return cls(entry, module)else:mod_path, _, cls_name = entry.rpartition('.')mod = import_module(mod_path)try:cls = getattr(mod, cls_name)except AttributeError:if module is None:# If importing as an app module failed, that error probably# contains the most informative traceback. Trigger it again.import_module(entry)else:raiseif not issubclass(cls, AppConfig):raise ImproperlyConfigured("'%s' isn't a subclass of AppConfig." % entry)try:app_name = cls.nameexcept 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)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)

首先module = import_module(entry)先加载模块,然后entry = module.default_app_config获取各个app的配置,我就举一个admin的例子把

class AdminConfig(SimpleAdminConfig):"""The default AppConfig for admin which does autodiscovery."""def ready(self):super(AdminConfig, self).ready()self.module.autodiscover()

接下来mod_path, _, cls_name = entry.rpartition('.')进行分割,我们来看下分割后的结果集

mod_path django.contrib.admin.apps
mod_path django.contrib.auth.apps
mod_path django.contrib.contenttypes.apps
mod_path django.contrib.sessions.apps
mod_path django.contrib.messages.apps
mod_path django.contrib.staticfiles.apps
mod_path app01.apps
mod_path rbac.apps
mod_path django.contrib.admin.apps
mod_path django.contrib.auth.apps
mod_path django.contrib.contenttypes.apps
mod_path django.contrib.sessions.apps
mod_path django.contrib.messages.apps
mod_path django.contrib.staticfiles.apps
mod_path app01.apps
mod_path rbac.apps
、、、、
、、、、

最后就app_module = import_module(app_name)加载,并且return cls(app_name, app_module)返回实例对象

当加载完毕后,进行导入各个app模块

 for app_config in self.app_configs.values():app_config.import_models()

然后修改其状态

 for app_config in self.get_app_configs():app_config.ready()
self.ready = True

最后看下构造的结果集

app_configs odict_values([<AdminConfig: admin>, <AuthConfig: auth>, <ContentTypesConfig: contenttypes>, <SessionsConfig: sessions>, <MessagesConfig: messages>, <StaticFilesConfig: staticfiles>, <App01Config: app01>, <RbacConfig: rbac>])

总结如下:
django调用python manage.py runserver启动项目时候,利用CommandParser构造并且解析数据,然后执行django.setup方法,调用django.apps模块来读取项目文件中的settings.py拿到这面这几个app,然后交给django.appsregistry.pyconfig.py来进行统一的配置加载,然后最后self.fetch_command(subcommand).run_from_argv(self.argv)加载

仓促完成,如有问题,评论留言,感谢~_~

django---加载INSTALLED_APPS的源码分析相关推荐

  1. 这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析

    前言 package com.jvm.classloader;class Father2{public static String strFather="HelloJVM_Father&qu ...

  2. jQuery deferred应用dom加载完毕详细源码分析(三)

    我承认上章ajax部分写得不好,不要怪我,它的ajax代码太多了,而且跨越大,方法跳跃多,实在不好排版与讲解,但如果你真正想研究源码并且仔细读了得话,你的 收获应该会很大,至少你明白了js的ajax是 ...

  3. 描述一下JAVA的加载过程_JVM源码分析之Java类的加载过程

    简书 占小狼 转载请注明原创出处,谢谢! 趁着年轻,多学习 背景 最近对Java细节的底层实现比较感兴趣,比如Java类文件是如何加载到虚拟机的,类对象和方法是以什么数据结构存在于虚拟机中?虚方法.实 ...

  4. 下血本买的!Flutter中网络图片加载和缓存源码分析,看看这篇文章吧!

    目录 想要成为一名优秀的Android开发,你需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样. PagerAdapter 介绍 ViwePager 缓存策略 ViewPager 布局处 ...

  5. clamav --reload 加载病毒库源码分析

    基本流程可以参考clamav中clamdscan --version 不生效 我们直接从解析command开始.parse_command函数返回COMMAND_RELOAD类型.然后进入execut ...

  6. Android开发必会技术!Flutter中网络图片加载和缓存源码分析,完整PDF

    起因 事情是这样的. 4年前毕业那会,呆在公司的短视频项目,做 视频.那会做得比抖音还早,但是由于短视频太烧钱了,项目被公司关掉了.当时需要开发横竖屏直播/异步视频的场景,就研究下了市场上的 app, ...

  7. Flutter中网络图片加载和缓存源码分析,踩坑了

    关于Android的近况 大家都知道,今年移动开发不那么火热了,完全没有了前两年Android开发那种火热的势头,如此同时,AI热火朝天,很多言论都说Android不行了.其实不光是Android,i ...

  8. 2款不同样式的CSS3 Loading加载动画 附源码

    原文:2款不同样式的CSS3 Loading加载动画 附源码 我们经常看到的Loading加载很多都是转圈圈的那种,今天我们来换一种有创意的CSS3 Loading加载动画,一种是声波形状的动画,另一 ...

  9. 2.2 LayoutInflater 加载布局文件源码

    LayoutInflater 加载布局文件源码 LayoutInflater是一个用于将xml布局文件加载为View或者ViewGroup对象的工具,我们可以称之为布局加载器. 获取LayoutInf ...

最新文章

  1. ubuntu 18.04.4 安装 bazel
  2. Foundation 框架 归档
  3. @include与jsp:include的区别
  4. nodejs模块笔记
  5. ustc小道消息20220120
  6. asp.net Page页面中的一个有用属性
  7. 中的实践 中兴_中兴通讯王卫斌:一步到位 构建5G 2B新商业
  8. Bash on Windows 抢鲜测试 -- 介绍及安装
  9. leetcode 383. 赎金信 思考分析
  10. 肖仰华 | 知识图谱研究的回顾与展望
  11. 支持高并发的IIS Web服务器常用设置 II
  12. python2.7安装scipy_在centOS上离安装Python2.7以及numpy,scipy,matplot,sklearn等
  13. SVM支持向量机:分类、回归和核函数
  14. Memcached安装及配置
  15. C# for循环①护栏长度 ②广场砖面积 ③判断闰年平年
  16. 皮肤变好必遵守洗脸九法
  17. 海康视频WEB插件 V1.5.2 开发总结
  18. mt7621芯片更换ram
  19. [转载]推荐的Windows7精简版资源
  20. 谷仓加密方式_谷仓紧缩谷物如何导致苹果计算机

热门文章

  1. angular——数据操作 及 管道
  2. maya中镜头场景的制作
  3. 使用telnet访问网站
  4. 学习笔记之Qt从入门到精通(三)
  5. JavaScript完整原型链图解
  6. 联想小新Air14使用傲梅分区助手进行硬盘克隆出现的问题,克隆完显示RAW格式解决方案,win10家庭版硬盘BitLocker上锁解锁方法
  7. Java项目:springboot园区管理系统
  8. 点聚weboffice打开服务器文件,对点聚weboffice插件的使用说明
  9. 模拟实验室合成器插件-Arturia Analog Lab v5.2.0 WiN
  10. CAD 批量提取点坐标,实现坐标的快速提取