django1.10框架了解

这篇博客详细介绍了Django框架的启动过程,从manage.py的执行开始,讲解了如何设置环境变量、载入settings模块,以及核心管理工具ManagementUtility的执行流程。内容涵盖了Django的各个关键组件,如Apps、Urls、Middleware、Database等,并深入到execute_from_command_line函数和BaseCommand的run_from_argv方法,展示了如何处理用户输入的命令并执行相应的管理命令。最后,文章提到了runserver命令的执行细节和数据库连接操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


记录一下django的启动过程,通常在开发阶段使用./manage.py  subcommand options启动项目。
import os
import sys


if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_demo.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)
查看manage.py文件,首先将本项目的settings文件位置放入系统变量之中,然后启动django.core.management中的execute_from_command_line方法,正式进入Django框架处理的过程。
框架的处理方法主要集中在django包中,我们看看该包中都含有写什么文件

简单介绍一下每个文件的用途:
Apps,处理app载入等问题,存储已经载入的所有app
Bin,django-admin文件,框架启动的入口之一
Conf,主要有两个作用:1) 处理全局配置, 比如数据库、加载的应用、MiddleWare等      2) 处理urls配置, 就是url与View的映射关系。
Contrib,由Django的开发者贡献的功能模块,不过既然都已经随版本发布,就表示是官方的。
Core,Django的核心处理库,包括url分析、处理请求、缓存等,其中处理请求是核心了,比如处理fastcgi就是由wsgi.py处理。
Db,顾名思义,处理与数据库相关的,就是ORM。
Dispatch,其实这不是Django原创,是pydispatch库,主要处理消费者-工作者模式。
Forms,处理html的表单,不用多介绍。
Http,request、response、cookie等处理
Middleware,中间件,就是处理HTTP的request和response的,类似插件。比如默认的common中间件的一个功能:当一个页面没有找对对应的pattern时,会自定加上?/?重新处理。比如访问/blog时,而定义的pattern是?^blog/$?,所以找不到对应的pattern,会自动再用/blog/查找,当然前提是APPEND_SLASH=True。
Template,Django的模板。
Templatetags,处理Application的tag的wrapper,就是将INSTALLED_APPS中所有的Templatetags目录添加到Django.Templatetags目录中,则当使用{{load blog}}记载tag时,就可以使用import Django.Templatetags.blog 方式加载了。不过这有一个问题,如果其他Application目录中也有blog.py,这会加载第一个出现blog.py的tag。其实在Django中,有许多需要处理重名的地方,比如Template,需要格外小心,这个后续在介绍。
Test,测试相关
Urls,url处理
Utils,公共库,很多公用的类都在放在这里。
Views,最基本的View方法。
接下来看看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方法
def execute(self):
        """
        Given the command-line arguments, this figures out which subcommand is
        being run, creates a parser appropriate to that command, and runs it.
        """
        try:
            subcommand = self.argv[1]
        except IndexError:
            subcommand = 'help'  # Display help if no arguments were given.

        # Preprocess options to extract --settings and --pythonpath.
        # These options could affect the commands that are available, so they
        # must be processed early.
        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:])
            handle_default_options(options)
        except CommandError:
            pass  # Ignore any option errors at this point.

        no_settings_commands = [
            'help', 'version', '--help', '--version', '-h',
            'compilemessages', 'makemessages',
            'startapp', 'startproject',
        ]

        try:
            settings.INSTALLED_APPS
        except ImproperlyConfigured as exc:
            self.settings_exception = exc
            # A handful of built-in management commands work without settings.
            # Load the default settings -- where INSTALLED_APPS is empty.
            if subcommand in no_settings_commands:
                settings.configure()

        if settings.configured:
            # Start the auto-reloading dev server even if the code is broken.
            # The hardcoded condition is a code smell but we can't rely on a
            # flag on the command class because we haven't located it yet.
            if subcommand == 'runserver' and '--noreload' not in self.argv:
                try:
                    autoreload.check_errors(django.setup)()
                except Exception:
                    # The exception will be raised later in the child process
                    # started by the autoreloader. Pretend it didn't happen by
                    # loading an empty list of applications.
                    apps.all_models = defaultdict(OrderedDict)
                    apps.app_configs = OrderedDict()
                    apps.apps_ready = apps.models_ready = apps.ready = True

            # In all other cases, django.setup() is required to succeed.
            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])
        # Special-cases: We want 'django-admin --version' and
        # 'django-admin --help' to work, for backwards compatibility.
        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)
subcommand用来存储用户输入的命令;parser用来检查命令附带的参数;handle_default_options方法用来载入settings
def handle_default_options(options):
    """
    Include any default options that all commands should accept here
    so that ManagementUtility can handle them before searching for
    user commands.
    """
    if options.settings:
        os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
    if options.pythonpath:
        sys.path.insert(0, options.pythonpath)
settings.INSTALLED_APPS用来载入用户配置文件中配置的所有app,根据from django.conf import settings查看得知settings = LazySettings(),该类具有__getattr__方法,所以这种用法相当于调用LazySettings().__getattr__(INSTALLED_APPS)。最终实现的功能是读取用户提供的配置文件,以及框架目录中的global_settings文件,所有配置项如果用户有配置则载入用户配置的内容,如果没有则载入框架提供的配置文件中的内容。
本文以用户键入./manage.py  runserver  0.0.0.0:8080为例,autoreload.check_errors(django.setup)()将django.setup命令运行是否正确放入检查之中
def setup(set_prefix=True):
    """
    Configure the settings (this happens as a side effect of accessing the
    first setting), configure logging and populate the app registry.
    Set the thread-local urlresolvers script prefix if `set_prefix` is True.
    """
    from django.apps import apps
    from django.conf import settings
    from django.urls import set_script_prefix
    from django.utils.encoding import force_text
    from django.utils.log import configure_logging

    configure_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)
配置log相关信息,设置当前目录前缀,apps = Apps(installed_apps=None),Apps初始化的时候定义了全局的多个字典、变量、线程锁等。populate方法循环载入之前从配置文件中收录的应用和模块,并初始化这个线程安全的对象。
self.autocomplete()做一些参数和系统配置以及操作系统匹配等工作。
self.fetch_command(subcommand).run_from_argv(self.argv)为重点,是用户输入的命令的真实入口。
def fetch_command(self, subcommand):
        """
        Tries to fetch the given subcommand, printing a message with the
        appropriate command called from the command line (usually
        "django-admin" or "manage.py") if it can't be found.
        """
        # Get commands outside of try block to prevent swallowing exceptions
        commands = get_commands()
        try:
            app_name = commands[subcommand]
        except KeyError:
            if os.environ.get('DJANGO_SETTINGS_MODULE'):
                # If `subcommand` is missing due to misconfigured settings, the
                # following line will retrigger an ImproperlyConfigured exception
                # (get_commands() swallows the original one) so the user is
                # informed about it.
                settings.INSTALLED_APPS
            else:
                sys.stderr.write("No Django settings specified.\n")
            sys.stderr.write(
                "Unknown command: %r\nType '%s help' for usage.\n"
                % (subcommand, self.prog_name)
            )
            sys.exit(1)
        if isinstance(app_name, BaseCommand):
            # If the command is already loaded, use it directly.
            klass = app_name
        else:
            klass = load_command_class(app_name, subcommand)
        return klass
其中fetch_command根据subcommand返回对应的命令处理文件,load_command_class函数返回module.Command(),在本例中就是self.fetch_command(subcommand)就是runserver.Command(),这是一个多态的实现。
根据该函数可以看出来,所有命令处理文件均放在django/core/management/commands下面,django适用的命名有:

runserver.Command()中没有run_from_argv方法,但是其继承了BaseCommand,所以我们查看BaseCommand().run_from_argv方法
def run_from_argv(self, argv):
        """
        Set up any environment changes requested (e.g., Python path
        and Django settings), then run this command. If the
        command raises a ``CommandError``, intercept it and print it sensibly
        to stderr. If the ``--traceback`` option is present or the raised
        ``Exception`` is not ``CommandError``, raise it.
        """
        self._called_from_command_line = True
        parser = self.create_parser(argv[0], argv[1])

        options = parser.parse_args(argv[2:])
        cmd_options = vars(options)
        # Move positional args out of options to mimic legacy optparse
        args = cmd_options.pop('args', ())
        handle_default_options(options)
        try:
            self.execute(*args, **cmd_options)
        except Exception as e:
            if options.traceback or not isinstance(e, CommandError):
                raise

            # SystemCheckError takes care of its own formatting.
            if isinstance(e, SystemCheckError):
                self.stderr.write(str(e), lambda x: x)
            else:
                self.stderr.write('%s: %s' % (e.__class__.__name__, e))
            sys.exit(1)
        finally:
            connections.close_all()
其中调用了execute方法,然后将连接关闭,此处为数据库连接,可从execute方法中看出程序进行了数据库连接操作。
def execute(self, *args, **options):
        """
        Try to execute this command, performing system checks if needed (as
        controlled by the ``requires_system_checks`` attribute, except if
        force-skipped).
        """
        if options['no_color']:
            self.style = no_style()
            self.stderr.style_func = None
        if options.get('stdout'):
            self.stdout = OutputWrapper(options['stdout'])
        if options.get('stderr'):
            self.stderr = OutputWrapper(options['stderr'], self.stderr.style_func)

        saved_locale = None
        if not self.leave_locale_alone:
            # Only mess with locales if we can assume we have a working
            # settings file, because django.utils.translation requires settings
            # (The final saying about whether the i18n machinery is active will be
            # found in the value of the USE_I18N setting)
            if not self.can_import_settings:
                raise CommandError("Incompatible values of 'leave_locale_alone' "
                                   "(%s) and 'can_import_settings' (%s) command "
                                   "options." % (self.leave_locale_alone,
                                                 self.can_import_settings))
            # Deactivate translations, because django-admin creates database
            # content like permissions, and those shouldn't contain any
            # translations.
            from django.utils import translation
            saved_locale = translation.get_language()
            translation.deactivate_all()

        try:
            if self.requires_system_checks and not options.get('skip_checks'):
                self.check()
            if self.requires_migrations_checks:
                self.check_migrations()
            output = self.handle(*args, **options)
            if output:
                if self.output_transaction:
                    connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
                    output = '%s\n%s\n%s' % (
                        self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),
                        output,
                        self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),
                    )
                self.stdout.write(output)
        finally:
            if saved_locale is not None:
                translation.activate(saved_locale)
        return output
self.handle方法对应的是runserver.Command()中的该方法,也就是runserver命令具体的工作内容了,该部分执行完成之后进行数据库连接。runserver具体执行稍后总结。至此所有django提供的命令均可以找到各自的入口了。














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值