文档章节

【Django源码浅析】-Django命令系统

Archer弓兵
 Archer弓兵
发布于 2017/04/09 18:38
字数 1248
阅读 69
收藏 0

鉴于笔者水平有限,文中不免出现一些错误,还请多多指教!

好了,下边是正文....

首先大概看一下Django 项目的主要目录,初步建立一下Django源码的世界观。

├── django          //工程代码存放路径
├── docs            //文档
├── extras          
├── js_tests        //测试
├── scripts         //脚本
└── tests           //单元测试

Django核心代码主要在django目录下边

django/
├── apps(app模块)
├── bin(可执行命令)
├── conf(配置)
├── contrib(其他开发者贡献代码)
├── core(核心组件)
├── db(ORM模块)
├── dispatch
├── forms(表单模块)
├── http
├── middleware(中间件)
├── template(模板)
├── templatetags
├── test(测试代码)
├── urls(url路由模块)
├── utils(工具代码)
└── views(视图模块)

在django中我们常用的命令主要有两个,一个是django-admin,一个是xxxx,我们先看一下django-admin

1、命令位置

lion@localhost:~/django/django$ whereis django-admin
django-admin: /usr/local/bin/django-admin /usr/local/bin/django-admin.py /usr/local/bin/django-admin.pyc

2、命令内容

lion@localhost:~/django/django$ cat /usr/local/bin/django-admin
#!/usr/bin/python

# -*- coding: utf-8 -*-
import re
import sys

from django.core.management import execute_from_command_line

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(execute_from_command_line())

其实对比不难发现,django-admin命令其实对应的是django源码中的.django/bin/django-admin.py这个文件。

django-admin.py 引用了django.core中的management,并调用了其execute_from_command_line函数。

注:在最新版中django-admin和manage.py中调用的都是execute_from_command_line函数了,较旧版本的django中可能不同。

所以要分析django的命令系统,就要从execute_from_command_line函数入手。

execute_from_command_line函数定义:

def execute_from_command_line(argv=None):
    """
    A simple method that runs a ManagementUtility.
    """
    utility = ManagementUtility(argv)
    utility.execute()

函数初始化ManagementUtility类,传入argv(也就是命令行参数)参数,并执行execute方法

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',
        'startapp', 'startproject', 'compilemessages',
    ]

    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)

此方法主要解析命令行参数,加载settings配置,如果setting配置成功则执行django.setup函数(此函数主要是加载App),最后一步调用的核心命令为fetch_command命令,并执行run_from_argv函数

先看一下fetch_command函数

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函数类似一个工厂函数,由get_commands函数扫描出所有的子命令,包括managemen中的子命令和app下的managemen中commands的子命令(自定义),然后根据传入的subcommand初始化Command类。

如果子命令不在commands字典内的话,会抛出一个“Unknown command”的提示,如果子命令存在则返回初始化的Command类。

接着视角在返回到execute函数中,接着

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

将会调用fetch_command(subcommand)初始化Command类的run_from_argv方法。run_from_argv由各个Command的基类BaseCommand定义,最终将会调用各个子类实现的handle方法。从而执行子命令的业务逻辑。

至此,命令调用的逻辑基本完成。

 

笔者随笔:

通过阅读这一部分的代码,其中最值得学习的地方在于fetch_commands函数,这是一个运用工厂方法的最佳实践,这样不但最大程度的解耦了代码实现,同时使得命令系统更易于扩展(App 自定义子命令就是一个很好的说明)

再有一点就是Command基类的定义,对于各种子命令的定义,基类完整的抽象出了command业务的工作逻辑,提供了统一的命令调用接口使得命令系统更易于扩展。

 

© 著作权归作者所有

共有 人打赏支持
下一篇: Docker
Archer弓兵
粉丝 5
博文 34
码字总数 14928
作品 0
杭州
程序员
私信 提问
Django1.8 django-admin和manage命令简要分析

startproject和startapp 创建一个项目(Project) Django使用django-admin startproject [projectname]命令生成project。 django-admin命令行的路径为:django/bin/django-admin.py 以djang......

donhui
2015/11/23
1K
3
PYTHON资源入口汇总

官网 入口 官方文档 英文 document 2.7.6 入口| 标准库 document 3.x 入口 The Hitchhiker’s Guide to Python 入口 Python Monk 入口 中文 document 2.7 入口 非官方 google的python文档 Go...

好铁
2016/02/12
99
0
Django学习笔记(一)

Django是python web框架中的一种,下面我就介绍一些自己在学习过程中遇到的问题。 官方网站:https://www.djangoproject.com,这个是我们在学习过程中需要重点参考的文档,而且我们需要经常翻...

彩虹的夜晚
2017/10/20
0
0
CentOS 6.4下安装Django

Django是一个基于python的网站开发框架,笔者学过了python的基本知识,对用python进行网站开发颇有兴趣。于是想安装一个在本地研究一下。 笔者本地操作系统是:CentOS 6.4,Linux内核:2.6.3...

临峰不畏
2014/06/08
0
0
Django 链接MySQL

Python版本:3.5.1 Django版本:1.9 前面将Python升级完,然后将pip安装,然后是安装Django,高高兴兴的使用命令: django-admin.py startproject first 把Django名为first的项目也建立了,然...

Edenwy
2017/06/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

0020-使用JDBC向Kudu表插入中文字符-双引号的秘密

1.问题描述 使用Impala JDBC向Kudu表中插入中文字符,插入的中文字符串乱码,中文字符串被截断。 2.问题复现 测试环境: CDH5.12.0 Kudu1.4.0 ImpalaJDBC41_2.5.35 1.使用ImpalaJDBC代码进行...

Hadoop实操
9分钟前
0
0
配置防盗链、访问控制Directory、访问控制FilesMatch

11月19日任务 11.25 配置防盗链 11.26 访问控制Directory 11.27 访问控制FilesMatch 一.配置防盗链 通过限制referer来实现防盗链的功能 配置文件增加如下内容 <Directory /data/wwwroot/www...

hhpuppy
10分钟前
1
0
Java内存管理 -JVM 垃圾回收

一.概述 相比起C和C++的自己回收内存,JAVA要方便得多,因为JVM会为我们自动分配内存以及回收内存。 在之前的JVM 之内存管理 中,我们介绍了JVM内存管理的几个区域,其中程序计数器以及虚拟机...

终日而思一
12分钟前
0
0
Nginx 服务器之速率限制

Nginx服务器有一个非常有用的限速功能,但是它却常常被错误配置。 这个功能用来限制用户在某此时间段内请求的的HTTP请求数,此请求应该是 GET 或POST 来发出的请求。 这个限速功能常常被应用...

架构师springboot
24分钟前
0
0
锁和分布式锁

锁的由来 : 多线程环境中,经常遇到多个线程访问同一个 共享资源 ,这时候作为开发者必须考虑如何维护数据一致性,这就需要某种机制来保证只有满足某个条件(获取锁成功)的线程才能访问资源...

微笑向暖wx
26分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部