django基本执行启动过程的源码简单分析和settings.py分环境配置
- 从django的配置文件加载说开去
我们在运行django的项目时,除了工程化的线上环境部署的时候,会利用supervisord或者gunicorn进行运行,在开发环境大多数会用命令行进行执行
python manage.py runserver 0:8080
如果需要指定settings文件,会这样运行
python manage.py runserver 0:8080 --settings=ldaptest.settings-prd
就能让django加载我们在settings.prd.py这个文件里定义的配置,这是怎么做到的呢
- django项目的简单执行流程
python manage.py runserver 0:8080 --settings=ldaptest.settings-prd 是怎么执行的呢?
我们看看manage.py的源码
#!/usr/bin/env python
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ldaptest.settings")
try:
from django.core.management import execute_from_command_line
except ImportError:
# The above import may fail for some other reason. Ensure that the
# issue is really that Django is missing to avoid masking other
# exceptions on Python 2.
try:
import django
except 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?"
)
raise
execute_from_command_line(sys.argv)
我们可以看到,实际执行的是execute_from_command_line方法,我们继续跟到这个函数
def execute_from_command_line(argv=None):
"""
A simple method that runs a ManagementUtility.
"""
utility = ManagementUtility(argv)
utility.execute()
可以看到,执行的是ManagementUtility的execute方法,我们继续跟到这个方法
看下代码片段:
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-all
try:
options, args = parser.parse_known_args(self.argv[2:])
print 'args:' #增加调试
print options, args #增加调试
handle_default_options(options)
except CommandError:
pass # Ignore any option errors at this point.
try:
settings.INSTALLED_APPS
except ImproperlyConfigured as exc:
self.settings_exception = exc
我们增加几行调试语句
其实这个打印的就是我们传入的命令行参数
我们执行 python manage.py runserver 0:8080 --settings=1
可以看到报错如下
可以看到,打印出了settings=1
我们继续跟代码,一层层往下跟,直到跟到异常抛出的地方
这个代码位于 django/conf/init.py
import importlib
import os
import time
from django.conf import global_settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.functional import LazyObject, empty
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE"
class Settings(object):
def __init__(self, settings_module):
# update this dict from global settings (but only for ALL_CAPS settings)
for setting in dir(global_settings):
if setting.isupper():
setattr(self, setting, getattr(global_settings, setting))
# store the settings module in case someone later cares
self.SETTINGS_MODULE = settings_module
mod = importlib.import_module(self.SETTINGS_MODULE)
tuple_settings = (
"INSTALLED_APPS",
"TEMPLATE_DIRS",
"LOCALE_PATHS",
)
self._explicit_settings = set()
for setting in dir(mod):
if setting.isupper():
setting_value = getattr(mod, setting)
if (setting in tuple_settings and
not isinstance(setting_value, (list, tuple))):
raise ImproperlyConfigured("The %s setting must be a list or a tuple. " % setting)
setattr(self, setting, setting_value)
self._explicit_settings.add(setting)
if not self.SECRET_KEY:
raise ImproperlyConfigured("The SECRET_KEY setting must not be empty.")
if hasattr(time, 'tzset') and self.TIME_ZONE:
# When we can, attempt to validate the timezone. If we can't find
# this file, no check happens and it's harmless.
zoneinfo_root = '/usr/share/zoneinfo'
if (os.path.exists(zoneinfo_root) and not
os.path.exists(os.path.join(zoneinfo_root, *(self.TIME_ZONE.split('/'))))):
raise ValueError("Incorrect timezone setting: %s" % self.TIME_ZONE)
# Move the time zone info into os.environ. See ticket #2315 for why
# we don't do this unconditionally (breaks Windows).
os.environ['TZ'] = self.TIME_ZONE
time.tzset()
def is_overridden(self, setting):
return setting in self._explicit_settings
def __repr__(self):
return '<%(cls)s "%(settings_module)s">' % {
'cls': self.__class__.__name__,
'settings_module': self.SETTINGS_MODULE,
}
可以看看这个代码都干了啥?
这个类的初始化部分,利用dir方法把django/conf/global_setting.py这个即这个类前面导入部分的
from django.conf import global_setting
用dir方法把这个模块的各种属性全部列出来,并且只取列出的属性中属性是大写的部分,并利用getattr和setattr把这些设置成Settings这个类的属性
这就是我们为什么可以利用变量定义一些程序中可能会用到的属性k-v值
比如,在settings.py里定义 mysqladdr = 1.2.3.4
则在程序里可以使用
from django.conf import settings
mysql_conn = settings.mysqladdr
则在程序里可以像上面这样使用的原因就在这了