~ubuntu-branches/ubuntu/jaunty/python-django/jaunty-backports

« back to all changes in this revision

Viewing changes to django/core/management/base.py

  • Committer: Bazaar Package Importer
  • Author(s): Scott James Remnant
  • Date: 2008-11-15 19:15:33 UTC
  • mfrom: (1.1.6 upstream)
  • Revision ID: james.westby@ubuntu.com-20081115191533-xbt1ut2xf4fvwtvc
Tags: 1.0.1-0ubuntu1
* New upstream release:
  - Bug fixes.

* The tests/ sub-directory appaers to have been dropped upstream, so pull
  our patch to workaround the tests and modify the rules.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
Base classes for writing management commands (named commands which can
 
3
be executed through ``django-admin.py`` or ``manage.py``).
 
4
 
 
5
"""
 
6
 
1
7
import os
2
8
import sys
3
9
from optparse import make_option, OptionParser
12
18
    from sets import Set as set     # For Python 2.3
13
19
 
14
20
class CommandError(Exception):
 
21
    """
 
22
    Exception class indicating a problem while executing a management
 
23
    command.
 
24
 
 
25
    If this exception is raised during the execution of a management
 
26
    command, it will be caught and turned into a nicely-printed error
 
27
    message to the appropriate output stream (i.e., stderr); as a
 
28
    result, raising this exception (with a sensible description of the
 
29
    error) is the preferred way to indicate that something has gone
 
30
    wrong in the execution of a command.
 
31
    
 
32
    """
15
33
    pass
16
34
 
17
35
def handle_default_options(options):
18
36
    """
19
 
    Include any default options that all commands should accept
20
 
    here so that ManagementUtility can handle them before searching
21
 
    for user commands.
 
37
    Include any default options that all commands should accept here
 
38
    so that ManagementUtility can handle them before searching for
 
39
    user commands.
 
40
    
22
41
    """
23
42
    if options.settings:
24
43
        os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
26
45
        sys.path.insert(0, options.pythonpath)
27
46
 
28
47
class BaseCommand(object):
 
48
    """
 
49
    The base class from which all management commands ultimately
 
50
    derive.
 
51
 
 
52
    Use this class if you want access to all of the mechanisms which
 
53
    parse the command-line arguments and work out what code to call in
 
54
    response; if you don't need to change any of that behavior,
 
55
    consider using one of the subclasses defined in this file.
 
56
 
 
57
    If you are interested in overriding/customizing various aspects of
 
58
    the command-parsing and -execution behavior, the normal flow works
 
59
    as follows:
 
60
 
 
61
    1. ``django-admin.py`` or ``manage.py`` loads the command class
 
62
       and calls its ``run_from_argv()`` method.
 
63
 
 
64
    2. The ``run_from_argv()`` method calls ``create_parser()`` to get
 
65
       an ``OptionParser`` for the arguments, parses them, performs
 
66
       any environment changes requested by options like
 
67
       ``pythonpath``, and then calls the ``execute()`` method,
 
68
       passing the parsed arguments.
 
69
 
 
70
    3. The ``execute()`` method attempts to carry out the command by
 
71
       calling the ``handle()`` method with the parsed arguments; any
 
72
       output produced by ``handle()`` will be printed to standard
 
73
       output and, if the command is intended to produce a block of
 
74
       SQL statements, will be wrapped in ``BEGIN`` and ``COMMIT``.
 
75
 
 
76
    4. If ``handle()`` raised a ``ComandError``, ``execute()`` will
 
77
       instead print an error message to ``stderr``.
 
78
 
 
79
    Thus, the ``handle()`` method is typically the starting point for
 
80
    subclasses; many built-in commands and command types either place
 
81
    all of their logic in ``handle()``, or perform some additional
 
82
    parsing work in ``handle()`` and then delegate from it to more
 
83
    specialized methods as needed.
 
84
 
 
85
    Several attributes affect behavior at various steps along the way:
 
86
    
 
87
    ``args``
 
88
        A string listing the arguments accepted by the command,
 
89
        suitable for use in help messages; e.g., a command which takes
 
90
        a list of application names might set this to '<appname
 
91
        appname ...>'.
 
92
 
 
93
    ``can_import_settings``
 
94
        A boolean indicating whether the command needs to be able to
 
95
        import Django settings; if ``True``, ``execute()`` will verify
 
96
        that this is possible before proceeding. Default value is
 
97
        ``True``.
 
98
 
 
99
    ``help``
 
100
        A short description of the command, which will be printed in
 
101
        help messages.
 
102
 
 
103
    ``option_list``
 
104
        This is the list of ``optparse`` options which will be fed
 
105
        into the command's ``OptionParser`` for parsing arguments.
 
106
 
 
107
    ``output_transaction``
 
108
        A boolean indicating whether the command outputs SQL
 
109
        statements; if ``True``, the output will automatically be
 
110
        wrapped with ``BEGIN;`` and ``COMMIT;``. Default value is
 
111
        ``False``.
 
112
 
 
113
    ``requires_model_validation``
 
114
        A boolean; if ``True``, validation of installed models will be
 
115
        performed prior to executing the command. Default value is
 
116
        ``True``. To validate an individual application's models
 
117
        rather than all applications' models, call
 
118
        ``self.validate(app)`` from ``handle()``, where ``app`` is the
 
119
        application's Python module.
 
120
    
 
121
    """
29
122
    # Metadata about this command.
30
123
    option_list = (
31
124
        make_option('--settings',
48
141
 
49
142
    def get_version(self):
50
143
        """
51
 
        Returns the Django version, which should be correct for all built-in
52
 
        Django commands. User-supplied commands should override this method.
 
144
        Return the Django version, which should be correct for all
 
145
        built-in Django commands. User-supplied commands should
 
146
        override this method.
 
147
        
53
148
        """
54
149
        return django.get_version()
55
150
 
56
151
    def usage(self, subcommand):
 
152
        """
 
153
        Return a brief description of how to use this command, by
 
154
        default from the attribute ``self.help``.
 
155
        
 
156
        """
57
157
        usage = '%%prog %s [options] %s' % (subcommand, self.args)
58
158
        if self.help:
59
159
            return '%s\n\n%s' % (usage, self.help)
61
161
            return usage
62
162
 
63
163
    def create_parser(self, prog_name, subcommand):
 
164
        """
 
165
        Create and return the ``OptionParser`` which will be used to
 
166
        parse the arguments to this command.
 
167
        
 
168
        """
64
169
        return OptionParser(prog=prog_name,
65
170
                            usage=self.usage(subcommand),
66
171
                            version=self.get_version(),
67
172
                            option_list=self.option_list)
68
173
 
69
174
    def print_help(self, prog_name, subcommand):
 
175
        """
 
176
        Print the help message for this command, derived from
 
177
        ``self.usage()``.
 
178
        
 
179
        """
70
180
        parser = self.create_parser(prog_name, subcommand)
71
181
        parser.print_help()
72
182
 
73
183
    def run_from_argv(self, argv):
 
184
        """
 
185
        Set up any environment changes requested (e.g., Python path
 
186
        and Django settings), then run this command.
 
187
        
 
188
        """
74
189
        parser = self.create_parser(argv[0], argv[1])
75
190
        options, args = parser.parse_args(argv[2:])
76
191
        handle_default_options(options)
77
192
        self.execute(*args, **options.__dict__)
78
193
 
79
194
    def execute(self, *args, **options):
 
195
        """
 
196
        Try to execute this command, performing model validation if
 
197
        needed (as controlled by the attribute
 
198
        ``self.requires_model_validation``). If the command raises a
 
199
        ``CommandError``, intercept it and print it sensibly to
 
200
        stderr.
 
201
        
 
202
        """
80
203
        # Switch to English, because django-admin.py creates database content
81
204
        # like permissions, and those shouldn't contain any translations.
82
205
        # But only do this if we can assume we have a working settings file,
110
233
    def validate(self, app=None, display_num_errors=False):
111
234
        """
112
235
        Validates the given app, raising CommandError for any errors.
113
 
 
 
236
        
114
237
        If app is None, then this will validate all installed apps.
 
238
        
115
239
        """
116
240
        from django.core.management.validation import get_validation_errors
117
241
        try:
128
252
            print "%s error%s found" % (num_errors, num_errors != 1 and 's' or '')
129
253
 
130
254
    def handle(self, *args, **options):
 
255
        """
 
256
        The actual logic of the command. Subclasses must implement
 
257
        this method.
 
258
        
 
259
        """
131
260
        raise NotImplementedError()
132
261
 
133
262
class AppCommand(BaseCommand):
 
263
    """
 
264
    A management command which takes one or more installed application
 
265
    names as arguments, and does something with each of them.
 
266
 
 
267
    Rather than implementing ``handle()``, subclasses must implement
 
268
    ``handle_app()``, which will be called once for each application.
 
269
    
 
270
    """
134
271
    args = '<appname appname ...>'
135
272
 
136
273
    def handle(self, *app_labels, **options):
149
286
        return '\n'.join(output)
150
287
 
151
288
    def handle_app(self, app, **options):
 
289
        """
 
290
        Perform the command's actions for ``app``, which will be the
 
291
        Python module corresponding to an application name given on
 
292
        the command line.
 
293
        
 
294
        """
152
295
        raise NotImplementedError()
153
296
 
154
297
class LabelCommand(BaseCommand):
 
298
    """
 
299
    A management command which takes one or more arbitrary arguments
 
300
    (labels) on the command line, and does something with each of
 
301
    them.
 
302
 
 
303
    Rather than implementing ``handle()``, subclasses must implement
 
304
    ``handle_label()``, which will be called once for each label.
 
305
 
 
306
    If the arguments should be names of installed applications, use
 
307
    ``AppCommand`` instead.
 
308
    
 
309
    """
155
310
    args = '<label label ...>'
156
311
    label = 'label'
157
312
 
167
322
        return '\n'.join(output)
168
323
 
169
324
    def handle_label(self, label, **options):
 
325
        """
 
326
        Perform the command's actions for ``label``, which will be the
 
327
        string as given on the command line.
 
328
        
 
329
        """
170
330
        raise NotImplementedError()
171
331
 
172
332
class NoArgsCommand(BaseCommand):
 
333
    """
 
334
    A command which takes no arguments on the command line.
 
335
 
 
336
    Rather than implementing ``handle()``, subclasses must implement
 
337
    ``handle_noargs()``; ``handle()`` itself is overridden to ensure
 
338
    no arguments are passed to the command.
 
339
 
 
340
    Attempting to pass arguments will raise ``CommandError``.
 
341
    
 
342
    """
173
343
    args = ''
174
344
 
175
345
    def handle(self, *args, **options):
178
348
        return self.handle_noargs(**options)
179
349
 
180
350
    def handle_noargs(self, **options):
 
351
        """
 
352
        Perform this command's actions.
 
353
        
 
354
        """
181
355
        raise NotImplementedError()
182
356
 
183
357
def copy_helper(style, app_or_project, name, directory, other_name=''):
184
358
    """
185
359
    Copies either a Django application layout template or a Django project
186
360
    layout template into the specified directory.
 
361
 
187
362
    """
188
363
    # style -- A color style object (see django.core.management.color).
189
364
    # app_or_project -- The string 'app' or 'project'.
194
369
    import re
195
370
    import shutil
196
371
    other = {'project': 'app', 'app': 'project'}[app_or_project]
197
 
    if not re.search(r'^\w+$', name): # If it's not a valid directory name.
198
 
        raise CommandError("%r is not a valid %s name. Please use only numbers, letters and underscores." % (name, app_or_project))
 
372
    if not re.search(r'^[_a-zA-Z]\w*$', name): # If it's not a valid directory name.
 
373
        # Provide a smart error message, depending on the error.
 
374
        if not re.search(r'^[_a-zA-Z]', name):
 
375
            message = 'make sure the name begins with a letter or underscore'
 
376
        else:
 
377
            message = 'use only numbers, letters and underscores'
 
378
        raise CommandError("%r is not a valid %s name. Please %s." % (name, app_or_project, message))
199
379
    top_dir = os.path.join(directory, name)
200
380
    try:
201
381
        os.mkdir(top_dir)
231
411
                sys.stderr.write(style.NOTICE("Notice: Couldn't set permission bits on %s. You're probably using an uncommon filesystem setup. No problem.\n" % path_new))
232
412
 
233
413
def _make_writeable(filename):
234
 
    "Makes sure that the file is writeable. Useful if our source is read-only."
 
414
    """
 
415
    Make sure that the file is writeable. Useful if our source is
 
416
    read-only.
 
417
    
 
418
    """
235
419
    import stat
236
420
    if sys.platform.startswith('java'):
237
421
        # On Jython there is no os.access()