Ticket #9170: management-commands-9170.diff

File management-commands-9170.diff, 9.7 KB (added by davidfischer, 6 years ago)

Added a complete tutorial of adding a management command

  • docs/howto/custom-management-commands.txt

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