Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #9170 -- added improved custom management command documentation.

Thanks to David Fischer and Eric Holscher for their work on initial patches.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@13138 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 0618cb24f595b5c8698cf6f4c3a2ccd3722160f0 1 parent b095813
Brian Rosner authored May 08, 2010

Showing 1 changed file with 232 additions and 10 deletions. Show diff stats Hide diff stats

  1. 242  docs/howto/custom-management-commands.txt
242  docs/howto/custom-management-commands.txt
... ...
@@ -1,5 +1,6 @@
1 1
 .. _howto-custom-management-commands:
2 2
 
  3
+====================================
3 4
 Writing custom django-admin commands
4 5
 ====================================
5 6
 
@@ -7,27 +8,248 @@ Writing custom django-admin commands
7 8
 
8 9
 Applications can register their own actions with ``manage.py``. For example,
9 10
 you might want to add a ``manage.py`` action for a Django app that you're
10  
-distributing.
  11
+distributing. In this document, we will be building a custom ``closepoll`` 
  12
+command for the ``polls`` application from the
  13
+:ref:`tutorial<intro-tutorial01>`.
11 14
 
12  
-To do this, just add a ``management/commands`` directory to your application.
  15
+To do this, just add a ``management/commands`` directory to the application.
13 16
 Each Python module in that directory will be auto-discovered and registered as
14 17
 a command that can be executed as an action when you run ``manage.py``::
15 18
 
16  
-    blog/
  19
+    polls/
17 20
         __init__.py
18 21
         models.py
19 22
         management/
20 23
             __init__.py
21 24
             commands/
22 25
                 __init__.py
23  
-                explode.py
  26
+                closepoll.py
  27
+        tests.py
24 28
         views.py
25 29
 
26  
-In this example, the ``explode`` command will be made available to any project
27  
-that includes the ``blog`` application in ``settings.INSTALLED_APPS``.
  30
+In this example, the ``closepoll`` command will be made available to any project
  31
+that includes the ``polls`` application in :setting:`INSTALLED_APPS`.
  32
+
  33
+The ``closepoll.py`` module has only one requirement -- it must define a class
  34
+``Command`` that extends :class:`BaseCommand` or one of its
  35
+:ref:`subclasses<ref-basecommand-subclasses>`.
  36
+
  37
+.. admonition:: Standalone scripts
  38
+
  39
+  Custom management commands are especially useful for running standalone
  40
+  scripts or for scripts that are periodically executed from the UNIX crontab
  41
+  or from Windows scheduled tasks control panel.
  42
+
  43
+To implement the command, edit ``polls/management/commands/closepoll.py`` to
  44
+look like this:
  45
+
  46
+.. code-block:: python
  47
+
  48
+    from django.core.management.base import BaseCommand, CommandError
  49
+    from example.polls.models import Poll
  50
+
  51
+    class Command(BaseCommand):
  52
+        args = '<poll_id poll_id ...>'
  53
+        help = 'Closes the specified poll for voting'
  54
+
  55
+        def handle(self, *args, **options):
  56
+            for poll_id in args:
  57
+                try:
  58
+                    poll = Poll.objects.get(pk=int(poll_id))
  59
+                except Poll.DoesNotExist:
  60
+                    raise CommandError('Poll "%s" does not exist' % poll_id)
  61
+
  62
+                poll.opened = False
  63
+                poll.save()
  64
+
  65
+                print 'Successfully closed poll "%s"' % poll_id
  66
+
  67
+The new custom command can be called using ``python manage.py closepoll 
  68
+<poll_id>``.
  69
+
  70
+The ``handle()`` method takes zero or more ``poll_ids`` and sets ``poll.opened``
  71
+to ``False`` for each one. If the user referenced any nonexistant polls, a
  72
+:class:`CommandError` is raised. The ``poll.opened`` attribute does not exist
  73
+in the :ref:`tutorial<intro-tutorial01>` and was added to
  74
+``polls.models.Poll`` for this example.
  75
+
  76
+The same ``closepoll`` could be easily modified to delete a given poll instead
  77
+of closing it by accepting additional command line options. These custom options
  78
+must be added to :attr:`~BaseCommand.option_list` like this:
  79
+
  80
+.. code-block:: python
  81
+
  82
+    from optparse import make_option
  83
+
  84
+    class Command(BaseCommand):
  85
+        option_list = BaseCommand.option_list + (
  86
+            make_option('--delete',
  87
+                action='store_true',
  88
+                dest='delete',
  89
+                default=False,
  90
+                help='Delete poll instead of closing it'),
  91
+            )
  92
+        # ...
  93
+
  94
+In addition to being able to add custom command line options, all 
  95
+:ref:`management commands<ref-django-admin>` can accept some 
  96
+default options such as :djadminopt:`--verbosity` and :djadminopt:`--traceback`.
  97
+
  98
+Command objects
  99
+===============
  100
+
  101
+.. class:: BaseCommand
  102
+
  103
+The base class from which all management commands ultimately derive.
  104
+
  105
+Use this class if you want access to all of the mechanisms which
  106
+parse the command-line arguments and work out what code to call in
  107
+response; if you don't need to change any of that behavior,
  108
+consider using one of its :ref:`subclasses<ref-basecommand-subclasses>`.
  109
+
  110
+Subclassing the :class:`BaseCommand` class requires that you implement the
  111
+:meth:`~BaseCommand.handle` method.
  112
+
  113
+Attributes
  114
+----------
  115
+
  116
+All attributes can be set in your derived class and can be used in 
  117
+:class:`BaseCommand`'s :ref:`subclasses<ref-basecommand-subclasses>`.
  118
+
  119
+.. attribute:: BaseCommand.args
  120
+
  121
+  A string listing the arguments accepted by the command,
  122
+  suitable for use in help messages; e.g., a command which takes
  123
+  a list of application names might set this to '<appname
  124
+  appname ...>'.
  125
+
  126
+.. attribute:: BaseCommand.can_import_settings
  127
+
  128
+  A boolean indicating whether the command needs to be able to
  129
+  import Django settings; if ``True``, ``execute()`` will verify
  130
+  that this is possible before proceeding. Default value is
  131
+  ``True``.
  132
+
  133
+.. attribute:: BaseCommand.help
  134
+
  135
+  A short description of the command, which will be printed in the
  136
+  help message when the user runs the command 
  137
+  ``python manage.py help <command>``.
  138
+
  139
+.. attribute:: BaseCommand.option_list
  140
+
  141
+  This is the list of ``optparse`` options which will be fed
  142
+  into the command's ``OptionParser`` for parsing arguments.
  143
+
  144
+.. attribute:: BaseCommand.output_transaction
  145
+
  146
+  A boolean indicating whether the command outputs SQL
  147
+  statements; if ``True``, the output will automatically be
  148
+  wrapped with ``BEGIN;`` and ``COMMIT;``. Default value is
  149
+  ``False``.
  150
+
  151
+.. attribute:: BaseCommand.requires_model_validation
  152
+
  153
+  A boolean; if ``True``, validation of installed models will be
  154
+  performed prior to executing the command. Default value is
  155
+  ``True``. To validate an individual application's models
  156
+  rather than all applications' models, call
  157
+  :meth:`~BaseCommand.validate` from :meth:`~BaseCommand.handle`.
  158
+
  159
+Methods
  160
+-------
  161
+
  162
+:class:`BaseCommand` has a few methods that can be overridden but only
  163
+the :meth:`~BaseCommand.handle` method must be implemented.
  164
+
  165
+.. admonition:: Implementing a constructor in a subclass
  166
+
  167
+  If you implement ``__init__`` in your subclass of :class:`BaseCommand`,
  168
+  you must call :class:`BaseCommand`'s ``__init__``.
  169
+
  170
+  .. code-block:: python
  171
+
  172
+    class Command(BaseCommand):
  173
+        def __init__(self, *args, **kwargs):
  174
+            super(Command, self).__init__(*args, **kwargs)
  175
+            # ...
  176
+
  177
+.. method:: BaseCommand.get_version()
  178
+
  179
+    Return the Django version, which should be correct for all
  180
+    built-in Django commands. User-supplied commands can
  181
+    override this method to return their own version.
  182
+
  183
+.. method:: BaseCommand.execute(*args, **options)
  184
+
  185
+    Try to execute this command, performing model validation if
  186
+    needed (as controlled by the attribute
  187
+    :attr:`requires_model_validation`). If the command raises a
  188
+    :class:`CommandError`, intercept it and print it sensibly to
  189
+    stderr.
  190
+
  191
+.. method:: BaseCommand.handle(*args, **options)
  192
+
  193
+    The actual logic of the command. Subclasses must implement this method.
  194
+
  195
+.. _ref-basecommand-subclasses:
  196
+
  197
+BaseCommand subclasses
  198
+----------------------
  199
+
  200
+.. class:: AppCommand
  201
+
  202
+A management command which takes one or more installed application
  203
+names as arguments, and does something with each of them.
  204
+
  205
+Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement
  206
+:meth:`~AppCommand.handle_app`, which will be called once for each application.
  207
+
  208
+.. method:: AppCommand.handle_app(app, **options)
  209
+
  210
+    Perform the command's actions for ``app``, which will be the
  211
+    Python module corresponding to an application name given on
  212
+    the command line.
  213
+
  214
+.. class:: LabelCommand
  215
+
  216
+A management command which takes one or more arbitrary arguments
  217
+(labels) on the command line, and does something with each of
  218
+them.
  219
+
  220
+Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement
  221
+:meth:`~LabelCommand.handle_label`, which will be called once for each label.
  222
+
  223
+.. method:: LabelCommand.handle_label(label, **options)
  224
+
  225
+    Perform the command's actions for ``label``, which will be the
  226
+    string as given on the command line.
  227
+
  228
+.. class:: NoArgsCommand
  229
+
  230
+A command which takes no arguments on the command line.
  231
+
  232
+Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement
  233
+:meth:`~NoArgsCommand.handle_noargs`; :meth:`~BaseCommand.handle` itself is 
  234
+overridden to ensure no arguments are passed to the command.
  235
+
  236
+.. method:: NoArgsCommand.handle_noargs(**options)
  237
+
  238
+    Perform this command's actions
  239
+
  240
+.. _ref-command-exceptions:
  241
+
  242
+Command exceptions
  243
+------------------
  244
+
  245
+.. class:: CommandError
28 246
 
29  
-The ``explode.py`` module has only one requirement -- it must define a class
30  
-called ``Command`` that extends ``django.core.management.base.BaseCommand``.
  247
+Exception class indicating a problem while executing a management
  248
+command.
31 249
 
32  
-For more details on how to define your own commands, look at the code for the
33  
-existing ``django-admin.py`` commands, in ``/django/core/management/commands``.
  250
+If this exception is raised during the execution of a management
  251
+command, it will be caught and turned into a nicely-printed error
  252
+message to the appropriate output stream (i.e., stderr); as a
  253
+result, raising this exception (with a sensible description of the
  254
+error) is the preferred way to indicate that something has gone
  255
+wrong in the execution of a command.

0 notes on commit 0618cb2

Please sign in to comment.
Something went wrong with that request. Please try again.