| 1 | import django
|
|---|
| 2 | from optparse import OptionParser
|
|---|
| 3 | import os
|
|---|
| 4 | import sys
|
|---|
| 5 | import textwrap
|
|---|
| 6 |
|
|---|
| 7 | # For backwards compatibility: get_version() used to be in this module.
|
|---|
| 8 | get_version = django.get_version
|
|---|
| 9 |
|
|---|
| 10 | def load_command_class(name):
|
|---|
| 11 | """
|
|---|
| 12 | Given a command name, returns the Command class instance. Raises
|
|---|
| 13 | ImportError if it doesn't exist.
|
|---|
| 14 | """
|
|---|
| 15 | # Let the ImportError propogate.
|
|---|
| 16 | return getattr(__import__('django.core.management.commands.%s' % name, {}, {}, ['Command']), 'Command')()
|
|---|
| 17 |
|
|---|
| 18 | def call_command(name, *args, **options):
|
|---|
| 19 | """
|
|---|
| 20 | Calls the given command, with the given options and args/kwargs.
|
|---|
| 21 |
|
|---|
| 22 | This is the primary API you should use for calling specific commands.
|
|---|
| 23 |
|
|---|
| 24 | Some examples:
|
|---|
| 25 | call_command('syncdb')
|
|---|
| 26 | call_command('shell', plain=True)
|
|---|
| 27 | call_command('sqlall', 'myapp')
|
|---|
| 28 | """
|
|---|
| 29 | klass = load_command_class(name)
|
|---|
| 30 | return klass.execute(*args, **options)
|
|---|
| 31 |
|
|---|
| 32 | class ManagementUtility(object):
|
|---|
| 33 | """
|
|---|
| 34 | Encapsulates the logic of the django-admin.py and manage.py utilities.
|
|---|
| 35 |
|
|---|
| 36 | A ManagementUtility has a number of commands, which can be manipulated
|
|---|
| 37 | by editing the self.commands dictionary.
|
|---|
| 38 | """
|
|---|
| 39 | def __init__(self):
|
|---|
| 40 | self.commands = self.default_commands()
|
|---|
| 41 |
|
|---|
| 42 | def default_commands(self):
|
|---|
| 43 | """
|
|---|
| 44 | Returns a dictionary of instances of all available Command classes.
|
|---|
| 45 |
|
|---|
| 46 | This works by looking for and loading all Python modules in the
|
|---|
| 47 | django.core.management.commands package.
|
|---|
| 48 |
|
|---|
| 49 | The dictionary is in the format {name: command_instance}.
|
|---|
| 50 | """
|
|---|
| 51 | command_dir = os.path.join(__path__[0], 'commands')
|
|---|
| 52 | names = [f[:-3] for f in os.listdir(command_dir) if not f.startswith('_') and f.endswith('.py')]
|
|---|
| 53 | return dict([(name, load_command_class(name)) for name in names])
|
|---|
| 54 |
|
|---|
| 55 | def usage(self):
|
|---|
| 56 | """
|
|---|
| 57 | Returns a usage string, for use with optparse.
|
|---|
| 58 |
|
|---|
| 59 | The string doesn't include the options (e.g., "--verbose"), because
|
|---|
| 60 | optparse puts those in automatically.
|
|---|
| 61 | """
|
|---|
| 62 | usage = ["%prog command [options]\nactions:"]
|
|---|
| 63 | commands = self.commands.items()
|
|---|
| 64 | commands.sort()
|
|---|
| 65 | for name, cmd in commands:
|
|---|
| 66 | usage.append(' %s %s' % (name, cmd.args))
|
|---|
| 67 | usage.extend(textwrap.wrap(cmd.help, initial_indent=' ', subsequent_indent=' '))
|
|---|
| 68 | usage.append('')
|
|---|
| 69 | return '\n'.join(usage[:-1]) # Cut off the last list element, an empty space.
|
|---|
| 70 |
|
|---|
| 71 | def execute(self, argv=None):
|
|---|
| 72 | """
|
|---|
| 73 | Parses the given argv from the command line, determines which command
|
|---|
| 74 | to run and runs the command.
|
|---|
| 75 | """
|
|---|
| 76 | if argv is None:
|
|---|
| 77 | argv = sys.argv
|
|---|
| 78 |
|
|---|
| 79 | # Create the parser object and parse the command-line args.
|
|---|
| 80 | # TODO: Ideally each Command class would register its own options for
|
|---|
| 81 | # add_option(), but we'd need to figure out how to allow for multiple
|
|---|
| 82 | # Commands using the same options. The optparse library gets in the way
|
|---|
| 83 | # by checking for conflicts:
|
|---|
| 84 | # http://docs.python.org/lib/optparse-conflicts-between-options.html
|
|---|
| 85 | parser = OptionParser(usage=self.usage(), version=get_version())
|
|---|
| 86 | parser.add_option('--settings',
|
|---|
| 87 | 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.')
|
|---|
| 88 | parser.add_option('--pythonpath',
|
|---|
| 89 | help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".')
|
|---|
| 90 | parser.add_option('--plain', action='store_true', dest='plain',
|
|---|
| 91 | help='When using "shell": Tells Django to use plain Python, not IPython.')
|
|---|
| 92 | parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
|
|---|
| 93 | help='Tells Django to NOT prompt the user for input of any kind.')
|
|---|
| 94 | parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True,
|
|---|
| 95 | help='When using "runserver": Tells Django to NOT use the auto-reloader.')
|
|---|
| 96 | parser.add_option('--format', default='json', dest='format',
|
|---|
| 97 | help='Specifies the output serialization format for fixtures')
|
|---|
| 98 | parser.add_option('--indent', default=None, dest='indent',
|
|---|
| 99 | type='int', help='Specifies the indent level to use when pretty-printing output')
|
|---|
| 100 | parser.add_option('--verbosity', action='store', dest='verbosity', default='1',
|
|---|
| 101 | type='choice', choices=['0', '1', '2'],
|
|---|
| 102 | help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
|
|---|
| 103 | parser.add_option('--adminmedia', dest='admin_media_path', default='',
|
|---|
| 104 | help='When using "runserver": Specifies the directory from which to serve admin media.')
|
|---|
| 105 | options, args = parser.parse_args(argv[1:])
|
|---|
| 106 |
|
|---|
| 107 | # If the 'settings' or 'pythonpath' options were submitted, activate those.
|
|---|
| 108 | if options.settings:
|
|---|
| 109 | os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
|
|---|
| 110 | if options.pythonpath:
|
|---|
| 111 | sys.path.insert(0, options.pythonpath)
|
|---|
| 112 |
|
|---|
| 113 | # Run the appropriate command.
|
|---|
| 114 | try:
|
|---|
| 115 | command_name = args[0]
|
|---|
| 116 | except IndexError:
|
|---|
| 117 | sys.stderr.write("Type '%s --help' for usage.\n" % os.path.basename(argv[0]))
|
|---|
| 118 | sys.exit(1)
|
|---|
| 119 | try:
|
|---|
| 120 | command = self.commands[command_name]
|
|---|
| 121 | except KeyError:
|
|---|
| 122 | sys.stderr.write("Unknown command: %r\nType '%s --help' for usage.\n" % (command_name, os.path.basename(argv[0])))
|
|---|
| 123 | sys.exit(1)
|
|---|
| 124 | command.execute(*args[1:], **options.__dict__)
|
|---|
| 125 |
|
|---|
| 126 | class ProjectManagementUtility(ManagementUtility):
|
|---|
| 127 | """
|
|---|
| 128 | A ManagementUtility that is specific to a particular Django project.
|
|---|
| 129 | As such, its commands are slightly different than those of its parent
|
|---|
| 130 | class.
|
|---|
| 131 |
|
|---|
| 132 | In practice, this class represents manage.py, whereas ManagementUtility
|
|---|
| 133 | represents django-admin.py.
|
|---|
| 134 | """
|
|---|
| 135 | def __init__(self, project_directory):
|
|---|
| 136 | super(ProjectManagementUtility, self).__init__()
|
|---|
| 137 |
|
|---|
| 138 | # Remove the "startproject" command from self.commands, because
|
|---|
| 139 | # that's a django-admin.py command, not a manage.py command.
|
|---|
| 140 | del self.commands['startproject']
|
|---|
| 141 |
|
|---|
| 142 | # Override the startapp command so that it always uses the
|
|---|
| 143 | # project_directory, not the current working directory (which is default).
|
|---|
| 144 | from django.core.management.commands.startapp import ProjectCommand
|
|---|
| 145 | self.commands['startapp'] = ProjectCommand(project_directory)
|
|---|
| 146 |
|
|---|
| 147 | def setup_environ(settings_mod):
|
|---|
| 148 | """
|
|---|
| 149 | Configure the runtime environment. This can also be used by external
|
|---|
| 150 | scripts wanting to set up a similar environment to manage.py.
|
|---|
| 151 | """
|
|---|
| 152 | # Add this project to sys.path so that it's importable in the conventional
|
|---|
| 153 | # way. For example, if this file (manage.py) lives in a directory
|
|---|
| 154 | # "myproject", this code would add "/path/to/myproject" to sys.path.
|
|---|
| 155 | project_directory, settings_filename = os.path.split(settings_mod.__file__)
|
|---|
| 156 | project_name = os.path.basename(project_directory)
|
|---|
| 157 | settings_name = os.path.splitext(settings_filename)[0]
|
|---|
| 158 | sys.path.append(os.path.join(project_directory, '..'))
|
|---|
| 159 | project_module = __import__(project_name, {}, {}, [''])
|
|---|
| 160 | sys.path.pop()
|
|---|
| 161 |
|
|---|
| 162 | # Set DJANGO_SETTINGS_MODULE appropriately.
|
|---|
| 163 | os.environ['DJANGO_SETTINGS_MODULE'] = '%s.%s' % (project_name, settings_name)
|
|---|
| 164 | return project_directory
|
|---|
| 165 |
|
|---|
| 166 | def execute_from_command_line(argv=None):
|
|---|
| 167 | """
|
|---|
| 168 | A simple method that runs a ManagementUtility.
|
|---|
| 169 | """
|
|---|
| 170 | utility = ManagementUtility()
|
|---|
| 171 | utility.execute(argv)
|
|---|
| 172 |
|
|---|
| 173 | def execute_manager(settings_mod, argv=None):
|
|---|
| 174 | """
|
|---|
| 175 | Like execute_from_command_line(), but for use by manage.py, a
|
|---|
| 176 | project-specific django-admin.py utility.
|
|---|
| 177 | """
|
|---|
| 178 | project_directory = setup_environ(settings_mod)
|
|---|
| 179 | utility = ProjectManagementUtility(project_directory)
|
|---|
| 180 | utility.execute(argv)
|
|---|