Django源码解析(三) Django开发服务器,WSGI规范实现
一. 什么是manage.py
在使用django-admin.py创建Django项目时,manage.py会被自动生成在项目根目录下.用以对django项目实现命令行操作.
调用django.core.management .execute_manager()方法实现命令执行.
#!/usr/bin/env python
from django.core.management import execute_manager
import imp
try:
imp.find_module('settings') # Assumed to be in the same directory.
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__)
sys.exit(1)
import settings
if __name__ == "__main__":
execute_manager(settings)
二. manage.py与django-admin.py的区别
django-admin.py调用django.core.management .execute_from_command_line()方法实现命令执行.
#!/usr/bin/env python
from django.core import management
if __name__ == "__main__":
management.execute_from_command_line()
manage.py与django-admin.py都使用了django.core.management包的方法.execute_manager()方法专门提供给manage.py使用,execute_from_command_line()专门
提供给django-admin.py使用.(地球人都看的出来,⊙﹏⊙b).
这俩个命令的主要差别在于execute_manager()多执行了一个setup_environ(settings_mod)方法.弄清这个方法的作用,就知道manage.py与django-admin.py的主要区别了.
def execute_manager(settings_mod, argv=None):
"""
Like execute_from_command_line(), but for use by manage.py, a
project-specific django-admin.py utility.
"""
setup_environ(settings_mod)
utility = ManagementUtility(argv)
utility.execute()
最主要显示setup_environ(settings_mod)方法功能的代码:
# Set DJANGO_SETTINGS_MODULE appropriately.
if original_settings_path:
os.environ['DJANGO_SETTINGS_MODULE'] = original_settings_path
else:
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
# Import the project module. We add the parent directory to PYTHONPATH to
# avoid some of the path errors new users can have.
sys.path.append(os.path.join(project_directory, os.pardir))
project_module = import_module(project_name)
sys.path.pop()
可见setup_environ(settings_mod)方法的主要功能,即manage.py与django-admin.py的主要区别:
manage.py设置一个名为DJANGO_SETTINGS_MODULE的系统环境变量.
例如我的django项目根目录为”E:\workspace\django\pearl”,settings模块的文件名为settings.py.DJANGO_SETTINGS_MODULE会被设置成为”pearl.settings”
-
manage.py把当前项目目录的父目录加入到PYTHONPATH,项目目录作为包导入.
例如我的django项目根目录为”E:\workspace\django\pearl”.把”E:\workspace\django”加入到PYTHONPATH,导入包名为”pearl”.
以上2点实际上使在使用manage.py时,不用像使用django-admin.py时输入”--settings”与”--pythonpath”参数.提供更便捷的操作.
注: 当直接使用django-admin.py时,需要设置'DJANGO_SETTINGS_MODULE'环境变量或”—settings”选项.
三. manage.py命令执行过程
通过execute_manager()与execute_from_command_line()方法,可以看到Django命令的执行主要通过django.core.management.ManagementUtility类执行.
ManagementUtility类的execute()方法为执行入口.
命令的执行过程:
1.解析命令,获得要执行的子命令名称.通过继承至OptionParser的类LaxOptionParser解析命令行.
2.获得所有可执行子命令的完整模块路径.
命令文件在django/core/management/commands目录下与INSTALLED_APPS(settings.py文件中定义)/management/commands目录下.
通过ManagementUtility.fetch_command()方法中,调用get_commands()方法实现.
3.返回子命令对应同名模块中的Command类的实例.
4.执行命令.调用Command类的实例的run_from_argv()方法.
def handle_default_options(options):
"""
此方法设定'DJANGO_SETTINGS_MODULE'与pythonpath,即在manage.py中设定的2个内容
"""
if options.settings:
os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
if options.pythonpath:
sys.path.insert(0, options.pythonpath)
class ManagementUtility(object):
"""
Encapsulates the logic of the django-admin.py and manage.py utilities.
A ManagementUtility has a number of commands, which can be manipulated
by editing the self.commands dictionary.
"""
def __init__(self, argv=None):
# 获取输入参数
self.argv = argv or sys.argv[:]
# 执行命令的文件,manage.py或django-admin.py
self.prog_name = os.path.basename(self.argv[0])
############################################################################
# 省略
############################################################################
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.py" or "manage.py") if it can't be found.
"""
try:
# 获得django/core/management/commands目录下与INSTALLED_APPS/management/commands目录下的子命令对应的模块前缀
# 如"cleanup"对应模块前缀"django.core"
app_name = get_commands()[subcommand]
except KeyError:
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:
# load_command_class的核心代码,返回指定子命令模块Command类的实例.
# module = import_module('%s.management.commands.%s' % (app_name, name))
# return module.Command()
klass = load_command_class(app_name, subcommand)
return klass
############################################################################
# 省略
############################################################################
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.
主要执行入口.
通过继承至OptionParser的类LaxOptionParser解析命令行.执行相应的子命令.
"""
# Preprocess options to extract --settings and --pythonpath.
# These options could affect the commands that are available, so they
# must be processed early.
parser = LaxOptionParser(usage="%prog subcommand [options] [args]",
version=get_version(),
option_list=BaseCommand.option_list)
self.autocomplete()
try:
options, args = parser.parse_args(self.argv)
# 如果输入了settings与pythonpath参数的话,设置这俩个参数
handle_default_options(options)
except:
pass # Ignore any option errors at this point.
try:
# 要执行的子命令 如: "manage.py runserver"中的"runserver"
subcommand = self.argv[1]
except IndexError:
# 未输入子命令的话,打印"help"信息
subcommand = 'help' # Display help if no arguments were given.
if subcommand == 'help':
if len(args) > 2:
self.fetch_command(args[2]).print_help(self.prog_name, args[2])
else:
parser.print_lax_help()
sys.stderr.write(self.main_help_text() + '\n')
sys.exit(1)
# Special-cases: We want 'django-admin.py --version' and
# 'django-admin.py --help' to work, for backwards compatibility.
elif self.argv[1:] == ['--version']:
# LaxOptionParser already takes care of printing the version.
pass
elif self.argv[1:] in (['--help'], ['-h']):
parser.print_lax_help()
sys.stderr.write(self.main_help_text() + '\n')
else:
# 找到相应子命令,并执行.
self.fetch_command(subcommand).run_from_argv(self.argv)
插播广告: settings.SETTINGS_MODULE
时常用到django.conf.settings的SETTINGS_MODULE属性来关联到我们自己项目的settings模块.它是如何实现的呢?
嘿嘿,这个是通过前面提到的DJANGO_SETTINGS_MODULE系统环境变量来实现的.来看这个实现的核心代码.
ENVIRONMENT_VARIABLE = "DJANGO_SETTINGS_MODULE" settings_module = os.environ[ENVIRONMENT_VARIABLE] self.SETTINGS_MODULE = settings_module
四. 命令
Django命令需要写在与命令行子命令同名的模块中.模块必须包含一个名为Command的类作为调用入口.
django.core.management.BaseCommand类作为所有命令的基类.还同时提供了AppCommand LabelCommand和NoArgsCommand三个辅助类.
自定义命令可继承自BaseCommand或其他三个辅助类,同时重写父类的不同方法即可.
简单介绍
1. BaseCommand
所有命令的基础类,预留一个handle()方法,供子类重写.
def handle(self, *args, **options):
"""
The actual logic of the command. Subclasses must implement
this method.
"""
raise NotImplementedError()
2. AppCommand
接受一个或多个installed application的名字作为第一部分参数.实现对App操作.预留一个handle_app()方法供子类重写.
def handle_app(self, app, **options):
"""
Perform the command's actions for ``app``, which will be the
Python module corresponding to an application name given on
the command line.
"""
raise NotImplementedError()
3. LabelCommand
接受一个或多个标签参数,预留一个handle_label()方法供子类重写.
def handle_label(self, label, **options):
"""
Perform the command's actions for ``label``, which will be the
string as given on the command line.
"""
raise NotImplementedError()
4. NoArgsCommand
不接受参数.预留一个handle_label()方法供子类重写.
def handle_noargs(self, **options):
"""
Perform this command's actions.
"""
raise NotImplementedError()