启动django测试服务的最简单方法无外乎,
python manage.py runserver
runserver是django默认提供的command,除了默认命令之外django支持自定义各种command。这种支持基于一定的约定,希望通过python manage.py运行的命令需要位于各app的management/commands目录下。
这非常方便,但也带来一定问题就是没法继续组织目录结构。commands下面的py文件很容易就会膨胀,所以必须自己对其进行一定扩展,使得我们能够在commands下面再任意拆分组织目录。
扩展的形式参照了默认command的运行方式,思路很简单:实现一个command,这个command接收参数,参数为真正要执行的command。
# -*- coding:utf-8 -*-
from django.core.management.base import BaseCommand
from django.utils.importlib import import_module
import importlib
import re
import os
class Command(BaseCommand):
'''
执行位于management/commands子目录中的其它命令
'''
def __init__(self):
super(Command, self).__init__()
def handle(self, *args, **options):
'''
python manage.py exec
'''
if len(args) == 0:
return
command_name = args[0]
self._exec_command(self._find_command(command_name), *args[1:], **options)
def _find_command(self, name):
'''
寻找可以执行的命令
'''
def _find_name(path):
return {path.split('/')[-1][:-3]:path}
paths = self._filter_paths(self._list_all_pyfiles())
commands = {}
for path in paths:
commands.update(_find_name(path))
return commands[name]
def _exec_command(self, command_path, *args, **options):
'''
执行命令
'''
def _build_import_name(path):
'''
从路径构造import需要的名字
'''
if path.startswith('/'):
path = path[1:-3]
return re.sub('/', '.', path)
packagename = _build_import_name(command_path)
instance = importlib.import_module(packagename)
command = instance.Command()
command.handle(*args, **options)
def _list_all_pyfiles(self):
'''
列出可能包含命令的路径
'''
def _find_packagedir():
'''
寻找项目目录
'''
try:
from django.conf import settings
module = import_module(settings.SETTINGS_MODULE)
project_directory = setup_environ(module, settings.SETTINGS_MODULE)
except (AttributeError, EnvironmentError, ImportError, KeyError):
logging_exception()
project_directory = ''
return project_directory
def _build_relative_path(result, project_dir):
'''
创建相对路径,移除项目目录
'''
length = len(project_dir)
return [item[length:] for item in result]
project_directory = _find_packagedir()
result = []
for base, folders, files in os.walk(project_directory):
# 过滤svn目录
if '.svn' in base:
continue
# 过滤非py文件
for file in files:
if not file.endswith('.py') or file.startswith('_'):
continue
result.append(os.path.join(base, file))
return _build_relative_path(result, project_directory)
def _filter_paths(self, paths):
'''
只处理自定义app中management/commands目录内的命令
'''
def _find_apps():
try:
from django.conf import settings
apps = settings.INSTALLED_APPS
except (AttributeError, EnvironmentError, ImportError):
apps = []
return apps
def _filter_path(path, apps):
def _filter(path, appname):
return path.startswith('/' + appname + '/management/commands/')
return reduce(lambda x, y: x or y, [_filter(path, appname) for appname in apps])
apps = _find_apps()
return [path for path in paths if _filter_path(path, apps)]