Skip to content
Permalink
Browse files

Major refactoring of django.core.management -- it's now a package rat…

…her than a 1730-line single module. All django-admin/manage.py commands are now stored in separate modules. This is backwards-incompatible for people who used django.core.management functions directly

git-svn-id: http://code.djangoproject.com/svn/django/trunk@5898 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
adrianholovaty committed Aug 16, 2007
1 parent 7f06e44 commit 01adbb55e6698b512ff202bc5fc81f9565e4003b
Showing with 1,972 additions and 1,771 deletions.
  1. +0 −1,730 django/core/management.py
  2. +180 −0 django/core/management/__init__.py
  3. +131 −0 django/core/management/base.py
  4. +28 −0 django/core/management/color.py
  5. 0 django/core/management/commands/__init__.py
  6. +33 −0 django/core/management/commands/adminindex.py
  7. +40 −0 django/core/management/commands/createcachetable.py
  8. +10 −0 django/core/management/commands/dbshell.py
  9. +32 −0 django/core/management/commands/diffsettings.py
  10. +33 −0 django/core/management/commands/dumpdata.py
  11. +64 −0 django/core/management/commands/flush.py
  12. +120 −0 django/core/management/commands/inspectdb.py
  13. +123 −0 django/core/management/commands/loaddata.py
  14. +47 −0 django/core/management/commands/reset.py
  15. +16 −0 django/core/management/commands/runfcgi.py
  16. +65 −0 django/core/management/commands/runserver.py
  17. +42 −0 django/core/management/commands/shell.py
  18. +10 −0 django/core/management/commands/sql.py
  19. +10 −0 django/core/management/commands/sqlall.py
  20. +10 −0 django/core/management/commands/sqlclear.py
  21. +10 −0 django/core/management/commands/sqlcustom.py
  22. +10 −0 django/core/management/commands/sqlflush.py
  23. +10 −0 django/core/management/commands/sqlindexes.py
  24. +7 −0 django/core/management/commands/sqlinitialdata.py
  25. +10 −0 django/core/management/commands/sqlreset.py
  26. +9 −0 django/core/management/commands/sqlsequencereset.py
  27. +33 −0 django/core/management/commands/startapp.py
  28. +39 −0 django/core/management/commands/startproject.py
  29. +129 −0 django/core/management/commands/syncdb.py
  30. +27 −0 django/core/management/commands/test.py
  31. +9 −0 django/core/management/commands/validate.py
  32. +420 −0 django/core/management/sql.py
  33. +221 −0 django/core/management/validation.py
  34. +6 −3 django/test/testcases.py
  35. +21 −21 django/test/utils.py
  36. +9 −9 tests/modeltests/fixtures/models.py
  37. +2 −2 tests/regressiontests/fixtures_regress/models.py
  38. +4 −4 tests/regressiontests/serializers_regress/tests.py
  39. +2 −2 tests/runtests.py

This file was deleted.

Oops, something went wrong.
@@ -0,0 +1,180 @@
import django
from optparse import OptionParser
import os
import sys
import textwrap

# For backwards compatibility: get_version() used to be in this module.
get_version = django.get_version

def load_command_class(name):
"""
Given a command name, returns the Command class instance. Raises
ImportError if it doesn't exist.
"""
# Let the ImportError propogate.
return getattr(__import__('django.core.management.commands.%s' % name, {}, {}, ['Command']), 'Command')()

def call_command(name, *args, **options):
"""
Calls the given command, with the given options and args/kwargs.
This is the primary API you should use for calling specific commands.
Some examples:
call_command('syncdb')
call_command('shell', plain=True)
call_command('sqlall', 'myapp')
"""
klass = load_command_class(name)
return klass.execute(*args, **options)

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):
self.commands = self.default_commands()

def default_commands(self):
"""
Returns a dictionary of instances of all available Command classes.
This works by looking for and loading all Python modules in the
django.core.management.commands package.
The dictionary is in the format {name: command_instance}.
"""
command_dir = os.path.join(__path__[0], 'commands')
names = [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')]
return dict([(name, load_command_class(name)) for name in names])

def usage(self):
"""
Returns a usage string, for use with optparse.
The string doesn't include the options (e.g., "--verbose"), because
optparse puts those in automatically.
"""
usage = ["%prog command [options]\nactions:"]
commands = self.commands.items()
commands.sort()
for name, cmd in commands:
usage.append(' %s %s' % (name, cmd.args))
usage.extend(textwrap.wrap(cmd.help, initial_indent=' ', subsequent_indent=' '))
usage.append('')
return '\n'.join(usage[:-1]) # Cut off the last list element, an empty space.

def execute(self, argv=None):
"""
Parses the given argv from the command line, determines which command
to run and runs the command.
"""
if argv is None:
argv = sys.argv

# Create the parser object and parse the command-line args.
# TODO: Ideally each Command class would register its own options for
# add_option(), but we'd need to figure out how to allow for multiple
# Commands using the same options. The optparse library gets in the way
# by checking for conflicts:
# http://docs.python.org/lib/optparse-conflicts-between-options.html
parser = OptionParser(usage=self.usage(), version=get_version())
parser.add_option('--settings',
help='The Python path to a settings module, e.g. "myproject.settings.main". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
parser.add_option('--pythonpath',
help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".')
parser.add_option('--plain', action='store_true', dest='plain',
help='When using "shell": Tells Django to use plain Python, not IPython.')
parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.')
parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True,
help='When using "runserver": Tells Django to NOT use the auto-reloader.')
parser.add_option('--format', default='json', dest='format',
help='Specifies the output serialization format for fixtures')
parser.add_option('--indent', default=None, dest='indent',
type='int', help='Specifies the indent level to use when pretty-printing output')
parser.add_option('--verbosity', action='store', dest='verbosity', default='1',
type='choice', choices=['0', '1', '2'],
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
parser.add_option('--adminmedia', dest='admin_media_path', default='',
help='When using "runserver": Specifies the directory from which to serve admin media.')
options, args = parser.parse_args(argv[1:])

# If the 'settings' or 'pythonpath' options were submitted, activate those.
if options.settings:
os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
if options.pythonpath:
sys.path.insert(0, options.pythonpath)

# Run the appropriate command.
try:
command_name = args[0]
except IndexError:
sys.stderr.write("Type '%s --help' for usage.\n" % os.path.basename(argv[0]))
sys.exit(1)
try:
command = self.commands[command_name]
except KeyError:
sys.stderr.write("Unknown command: %r\nType '%s --help' for usage.\n" % (command_name, os.path.basename(argv[0])))
sys.exit(1)
command.execute(*args[1:], **options.__dict__)

class ProjectManagementUtility(ManagementUtility):
"""
A ManagementUtility that is specific to a particular Django project.
As such, its commands are slightly different than those of its parent
class.
In practice, this class represents manage.py, whereas ManagementUtility
represents django-admin.py.
"""
def __init__(self, project_directory):
super(ProjectManagementUtility, self).__init__()

# Remove the "startproject" command from self.commands, because
# that's a django-admin.py command, not a manage.py command.
del self.commands['startproject']

# Override the startapp command so that it always uses the
# project_directory, not the current working directory (which is default).
from django.core.management.commands.startapp import ProjectCommand
self.commands['startapp'] = ProjectCommand(project_directory)

def setup_environ(settings_mod):
"""
Configure the runtime environment. This can also be used by external
scripts wanting to set up a similar environment to manage.py.
"""
# Add this project to sys.path so that it's importable in the conventional
# way. For example, if this file (manage.py) lives in a directory
# "myproject", this code would add "/path/to/myproject" to sys.path.
project_directory, settings_filename = os.path.split(settings_mod.__file__)
project_name = os.path.basename(project_directory)
settings_name = os.path.splitext(settings_filename)[0]
sys.path.append(os.path.join(project_directory, '..'))
project_module = __import__(project_name, {}, {}, [''])
sys.path.pop()

# Set DJANGO_SETTINGS_MODULE appropriately.
os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
return project_directory

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

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.
"""
project_directory = setup_environ(settings_mod)
utility = ProjectManagementUtility(project_directory)
utility.execute(argv)
@@ -0,0 +1,131 @@
from django.core.exceptions import ImproperlyConfigured
from django.core.management.color import color_style
import sys

class CommandError(Exception):
pass

class BaseCommand(object):
# Metadata about this command.
help = ''
args = ''

# Configuration shortcuts that alter various logic.
can_import_settings = True
requires_model_validation = True
output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;"

def __init__(self):
self.style = color_style()

def execute(self, *args, **options):
# Switch to English, because django-admin.py creates database content
# like permissions, and those shouldn't contain any translations.
# But only do this if we can assume we have a working settings file,
# because django.utils.translation requires settings.
if self.can_import_settings:
from django.utils import translation
translation.activate('en-us')

try:
if self.requires_model_validation:
self.validate()
output = self.handle(*args, **options)
if output:
if self.output_transaction:
# This needs to be imported here, because it relies on settings.
from django.db import backend
if backend.get_start_transaction_sql():
print self.style.SQL_KEYWORD(backend.get_start_transaction_sql())
print output
if self.output_transaction:
print self.style.SQL_KEYWORD("COMMIT;")
except CommandError, e:
sys.stderr.write(self.style.ERROR(str('Error: %s\n' % e)))
sys.exit(1)

def validate(self, app=None):
"""
Validates the given app, raising CommandError for any errors.
If app is None, then this will validate all installed apps.
"""
from django.core.management.validation import get_validation_errors
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
s = StringIO()
num_errors = get_validation_errors(s, app)
if num_errors:
s.seek(0)
error_text = s.read()
raise CommandError("One or more models did not validate:\n%s" % error_text)

def handle(self, *args, **options):
raise NotImplementedError()

class AppCommand(BaseCommand):
args = '[appname ...]'

def handle(self, *app_labels, **options):
from django.db import models
if not app_labels:
raise CommandError('Enter at least one appname.')
try:
app_list = [models.get_app(app_label) for app_label in app_labels]
except (ImproperlyConfigured, ImportError), e:
raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
output = []
for app in app_list:
app_output = self.handle_app(app, **options)
if app_output:
output.append(app_output)
return '\n'.join(output)

def handle_app(self, app, **options):
raise NotImplementedError()

class CopyFilesCommand(BaseCommand):
requires_model_validation = False

def copy_helper(self, app_or_project, name, directory, other_name=''):
import django
import os
import re
import shutil
other = {'project': 'app', 'app': 'project'}[app_or_project]
if not re.search(r'^\w+$', name): # If it's not a valid directory name.
raise CommandError("%r is not a valid %s name. Please use only numbers, letters and underscores." % (name, app_or_project))
top_dir = os.path.join(directory, name)
try:
os.mkdir(top_dir)
except OSError, e:
raise CommandError(e)

# Determine where the app or project templates are. Use
# django.__path__[0] because we don't know into which directory
# django has been installed.
template_dir = os.path.join(django.__path__[0], 'conf', '%s_template' % app_or_project)

for d, subdirs, files in os.walk(template_dir):
relative_dir = d[len(template_dir)+1:].replace('%s_name' % app_or_project, name)
if relative_dir:
os.mkdir(os.path.join(top_dir, relative_dir))
for i, subdir in enumerate(subdirs):
if subdir.startswith('.'):
del subdirs[i]
for f in files:
if f.endswith('.pyc'):
continue
path_old = os.path.join(d, f)
path_new = os.path.join(top_dir, relative_dir, f.replace('%s_name' % app_or_project, name))
fp_old = open(path_old, 'r')
fp_new = open(path_new, 'w')
fp_new.write(fp_old.read().replace('{{ %s_name }}' % app_or_project, name).replace('{{ %s_name }}' % other, other_name))
fp_old.close()
fp_new.close()
try:
shutil.copymode(path_old, path_new)
except OSError:
sys.stderr.write(self.style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new))
@@ -0,0 +1,28 @@
"""
Sets up the terminal color scheme.
"""

from django.utils import termcolors
import sys

def color_style():
"Returns a Style object with the Django color scheme."
if sys.platform == 'win32' or sys.platform == 'Pocket PC' or not sys.stdout.isatty():
return no_style()
class dummy: pass
style = dummy()
style.ERROR = termcolors.make_style(fg='red', opts=('bold',))
style.ERROR_OUTPUT = termcolors.make_style(fg='red', opts=('bold',))
style.NOTICE = termcolors.make_style(fg='red')
style.SQL_FIELD = termcolors.make_style(fg='green', opts=('bold',))
style.SQL_COLTYPE = termcolors.make_style(fg='green')
style.SQL_KEYWORD = termcolors.make_style(fg='yellow')
style.SQL_TABLE = termcolors.make_style(opts=('bold',))
return style

def no_style():
"Returns a Style object that has no colors."
class dummy:
def __getattr__(self, attr):
return lambda x: x
return dummy()
No changes.
@@ -0,0 +1,33 @@
from django.core.management.base import AppCommand
from django.utils.text import capfirst

MODULE_TEMPLATE = ''' {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(changeperm)s %%}
<tr>
<th>{%% if perms.%(app)s.%(changeperm)s %%}<a href="%(app)s/%(mod)s/">{%% endif %%}%(name)s{%% if perms.%(app)s.%(changeperm)s %%}</a>{%% endif %%}</th>
<td class="x50">{%% if perms.%(app)s.%(addperm)s %%}<a href="%(app)s/%(mod)s/add/" class="addlink">{%% endif %%}Add{%% if perms.%(app)s.%(addperm)s %%}</a>{%% endif %%}</td>
<td class="x75">{%% if perms.%(app)s.%(changeperm)s %%}<a href="%(app)s/%(mod)s/" class="changelink">{%% endif %%}Change{%% if perms.%(app)s.%(changeperm)s %%}</a>{%% endif %%}</td>
</tr>
{%% endif %%}'''

class Command(AppCommand):
help = 'Prints the admin-index template snippet for the given app name(s).'

def handle_app(self, app, **options):
from django.db.models import get_models
output = []
app_models = get_models(app)
app_label = app_models[0]._meta.app_label
output.append('{%% if perms.%s %%}' % app_label)
output.append('<div class="module"><h2>%s</h2><table>' % app_label.title())
for model in app_models:
if model._meta.admin:
output.append(MODULE_TEMPLATE % {
'app': app_label,
'mod': model._meta.module_name,
'name': capfirst(model._meta.verbose_name_plural),
'addperm': model._meta.get_add_permission(),
'changeperm': model._meta.get_change_permission(),
})
output.append('</table></div>')
output.append('{% endif %}')
return '\n'.join(output)
Oops, something went wrong.

0 comments on commit 01adbb5

Please sign in to comment.
You can’t perform that action at this time.