django基本执行启动过程的源码简单分析和settings.py分环境配置

Posted haozike

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了django基本执行启动过程的源码简单分析和settings.py分环境配置相关的知识,希望对你有一定的参考价值。

  1. 从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这个文件里定义的配置,这是怎么做到的呢

  1. 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

则在程序里可以像上面这样使用的原因就在这了

















以上是关于django基本执行启动过程的源码简单分析和settings.py分环境配置的主要内容,如果未能解决你的问题,请参考以下文章

Django源码解析:启动程序

Django如何启动源码分析

django---加载INSTALLED_APPS的源码分析

Spring启动过程源码分析基本概念

Spring启动过程源码分析基本概念

Spring启动过程源码分析基本概念