django---加载INSTALLED_APPS的源码分析
运行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=argv
和self.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
,然后返回一个函数wrapper
,autoreload.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.apps
的registry.py
和config.py
来进行统一的配置加载,然后最后self.fetch_command(subcommand).run_from_argv(self.argv)
加载
仓促完成,如有问题,评论留言,感谢~_~
django---加载INSTALLED_APPS的源码分析相关推荐
- 这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析
前言 package com.jvm.classloader;class Father2{public static String strFather="HelloJVM_Father&qu ...
- jQuery deferred应用dom加载完毕详细源码分析(三)
我承认上章ajax部分写得不好,不要怪我,它的ajax代码太多了,而且跨越大,方法跳跃多,实在不好排版与讲解,但如果你真正想研究源码并且仔细读了得话,你的 收获应该会很大,至少你明白了js的ajax是 ...
- 描述一下JAVA的加载过程_JVM源码分析之Java类的加载过程
简书 占小狼 转载请注明原创出处,谢谢! 趁着年轻,多学习 背景 最近对Java细节的底层实现比较感兴趣,比如Java类文件是如何加载到虚拟机的,类对象和方法是以什么数据结构存在于虚拟机中?虚方法.实 ...
- 下血本买的!Flutter中网络图片加载和缓存源码分析,看看这篇文章吧!
目录 想要成为一名优秀的Android开发,你需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样. PagerAdapter 介绍 ViwePager 缓存策略 ViewPager 布局处 ...
- clamav --reload 加载病毒库源码分析
基本流程可以参考clamav中clamdscan --version 不生效 我们直接从解析command开始.parse_command函数返回COMMAND_RELOAD类型.然后进入execut ...
- Android开发必会技术!Flutter中网络图片加载和缓存源码分析,完整PDF
起因 事情是这样的. 4年前毕业那会,呆在公司的短视频项目,做 视频.那会做得比抖音还早,但是由于短视频太烧钱了,项目被公司关掉了.当时需要开发横竖屏直播/异步视频的场景,就研究下了市场上的 app, ...
- Flutter中网络图片加载和缓存源码分析,踩坑了
关于Android的近况 大家都知道,今年移动开发不那么火热了,完全没有了前两年Android开发那种火热的势头,如此同时,AI热火朝天,很多言论都说Android不行了.其实不光是Android,i ...
- 2款不同样式的CSS3 Loading加载动画 附源码
原文:2款不同样式的CSS3 Loading加载动画 附源码 我们经常看到的Loading加载很多都是转圈圈的那种,今天我们来换一种有创意的CSS3 Loading加载动画,一种是声波形状的动画,另一 ...
- 2.2 LayoutInflater 加载布局文件源码
LayoutInflater 加载布局文件源码 LayoutInflater是一个用于将xml布局文件加载为View或者ViewGroup对象的工具,我们可以称之为布局加载器. 获取LayoutInf ...
最新文章
- ubuntu 18.04.4 安装 bazel
- Foundation 框架 归档
- @include与jsp:include的区别
- nodejs模块笔记
- ustc小道消息20220120
- asp.net Page页面中的一个有用属性
- 中的实践 中兴_中兴通讯王卫斌:一步到位 构建5G 2B新商业
- Bash on Windows 抢鲜测试 -- 介绍及安装
- leetcode 383. 赎金信 思考分析
- 肖仰华 | 知识图谱研究的回顾与展望
- 支持高并发的IIS Web服务器常用设置 II
- python2.7安装scipy_在centOS上离安装Python2.7以及numpy,scipy,matplot,sklearn等
- SVM支持向量机:分类、回归和核函数
- Memcached安装及配置
- C# for循环①护栏长度 ②广场砖面积 ③判断闰年平年
- 皮肤变好必遵守洗脸九法
- 海康视频WEB插件 V1.5.2 开发总结
- mt7621芯片更换ram
- [转载]推荐的Windows7精简版资源
- 谷仓加密方式_谷仓紧缩谷物如何导致苹果计算机