123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337 |
- ====================================
- Writing custom django-admin commands
- ====================================
- .. module:: django.core.management
- Applications can register their own actions with ``manage.py``. For example,
- you might want to add a ``manage.py`` action for a Django app that you're
- distributing. In this document, we will be building a custom ``closepoll``
- command for the ``polls`` application from the
- :doc:`tutorial</intro/tutorial01>`.
- To do this, just add a ``management/commands`` directory to the application.
- Django will register a ``manage.py`` command for each Python module in that
- directory whose name doesn't begin with an underscore. For example::
- polls/
- __init__.py
- models.py
- management/
- __init__.py
- commands/
- __init__.py
- _private.py
- closepoll.py
- tests.py
- views.py
- In this example, the ``closepoll`` command will be made available to any project
- that includes the ``polls`` application in :setting:`INSTALLED_APPS`.
- The ``_private.py`` module will not be available as a management command.
- The ``closepoll.py`` module has only one requirement -- it must define a class
- ``Command`` that extends :class:`BaseCommand` or one of its
- :ref:`subclasses<ref-basecommand-subclasses>`.
- .. admonition:: Standalone scripts
- Custom management commands are especially useful for running standalone
- scripts or for scripts that are periodically executed from the UNIX crontab
- or from Windows scheduled tasks control panel.
- To implement the command, edit ``polls/management/commands/closepoll.py`` to
- look like this:
- .. code-block:: python
- from django.core.management.base import BaseCommand, CommandError
- from polls.models import Poll
- class Command(BaseCommand):
- args = '<poll_id poll_id ...>'
- help = 'Closes the specified poll for voting'
- def handle(self, *args, **options):
- for poll_id in args:
- try:
- poll = Poll.objects.get(pk=int(poll_id))
- except Poll.DoesNotExist:
- raise CommandError('Poll "%s" does not exist' % poll_id)
- poll.opened = False
- poll.save()
- self.stdout.write('Successfully closed poll "%s"' % poll_id)
- .. note::
- When you are using management commands and wish to provide console
- output, you should write to ``self.stdout`` and ``self.stderr``,
- instead of printing to ``stdout`` and ``stderr`` directly. By
- using these proxies, it becomes much easier to test your custom
- command.
- The new custom command can be called using ``python manage.py closepoll
- <poll_id>``.
- The ``handle()`` method takes zero or more ``poll_ids`` and sets ``poll.opened``
- to ``False`` for each one. If the user referenced any nonexistent polls, a
- :class:`CommandError` is raised. The ``poll.opened`` attribute does not exist
- in the :doc:`tutorial</intro/tutorial01>` and was added to
- ``polls.models.Poll`` for this example.
- The same ``closepoll`` could be easily modified to delete a given poll instead
- of closing it by accepting additional command line options. These custom options
- must be added to :attr:`~BaseCommand.option_list` like this:
- .. code-block:: python
- from optparse import make_option
- class Command(BaseCommand):
- option_list = BaseCommand.option_list + (
- make_option('--delete',
- action='store_true',
- dest='delete',
- default=False,
- help='Delete poll instead of closing it'),
- )
- def handle(self, *args, **options):
- # ...
- if options['delete']:
- poll.delete()
- # ...
- The option (``delete`` in our example) is available in the options dict
- parameter of the handle method. See the :py:mod:`optparse` Python documentation
- for more about ``make_option`` usage.
- In addition to being able to add custom command line options, all
- :doc:`management commands</ref/django-admin>` can accept some
- default options such as :djadminopt:`--verbosity` and :djadminopt:`--traceback`.
- .. admonition:: Management commands and locales
- The :meth:`BaseCommand.execute` method sets the hardcoded ``en-us`` locale
- because the commands shipped with Django perform several tasks
- (for example, user-facing content rendering and database population) that
- require a system-neutral string language (for which we use ``en-us``).
- If your custom management command uses another locale, you should manually
- activate and deactivate it in your :meth:`~BaseCommand.handle` or
- :meth:`~NoArgsCommand.handle_noargs` method using the functions provided by
- the I18N support code:
- .. code-block:: python
- from django.core.management.base import BaseCommand, CommandError
- from django.utils import translation
- class Command(BaseCommand):
- ...
- can_import_settings = True
- def handle(self, *args, **options):
- # Activate a fixed locale, e.g. Russian
- translation.activate('ru')
- # Or you can activate the LANGUAGE_CODE
- # chosen in the settings:
- #
- #from django.conf import settings
- #translation.activate(settings.LANGUAGE_CODE)
- # Your command logic here
- # ...
- translation.deactivate()
- Take into account though, that system management commands typically have to
- be very careful about running in non-uniform locales, so:
- * Make sure the :setting:`USE_I18N` setting is always ``True`` when running
- the command (this is one good example of the potential problems stemming
- from a dynamic runtime environment that Django commands avoid offhand by
- always using a fixed locale).
- * Review the code of your command and the code it calls for behavioral
- differences when locales are changed and evaluate its impact on
- predictable behavior of your command.
- Command objects
- ===============
- .. class:: BaseCommand
- The base class from which all management commands ultimately derive.
- Use this class if you want access to all of the mechanisms which
- parse the command-line arguments and work out what code to call in
- response; if you don't need to change any of that behavior,
- consider using one of its :ref:`subclasses<ref-basecommand-subclasses>`.
- Subclassing the :class:`BaseCommand` class requires that you implement the
- :meth:`~BaseCommand.handle` method.
- Attributes
- ----------
- All attributes can be set in your derived class and can be used in
- :class:`BaseCommand`'s :ref:`subclasses<ref-basecommand-subclasses>`.
- .. attribute:: BaseCommand.args
- A string listing the arguments accepted by the command,
- suitable for use in help messages; e.g., a command which takes
- a list of application names might set this to '<appname
- appname ...>'.
- .. attribute:: BaseCommand.can_import_settings
- A boolean indicating whether the command needs to be able to
- import Django settings; if ``True``, ``execute()`` will verify
- that this is possible before proceeding. Default value is
- ``True``.
- .. attribute:: BaseCommand.help
- A short description of the command, which will be printed in the
- help message when the user runs the command
- ``python manage.py help <command>``.
- .. attribute:: BaseCommand.option_list
- This is the list of ``optparse`` options which will be fed
- into the command's ``OptionParser`` for parsing arguments.
- .. attribute:: BaseCommand.output_transaction
- A boolean indicating whether the command outputs SQL
- statements; if ``True``, the output will automatically be
- wrapped with ``BEGIN;`` and ``COMMIT;``. Default value is
- ``False``.
- .. attribute:: BaseCommand.requires_model_validation
- A boolean; if ``True``, validation of installed models will be
- performed prior to executing the command. Default value is
- ``True``. To validate an individual application's models
- rather than all applications' models, call
- :meth:`~BaseCommand.validate` from :meth:`~BaseCommand.handle`.
- Methods
- -------
- :class:`BaseCommand` has a few methods that can be overridden but only
- the :meth:`~BaseCommand.handle` method must be implemented.
- .. admonition:: Implementing a constructor in a subclass
- If you implement ``__init__`` in your subclass of :class:`BaseCommand`,
- you must call :class:`BaseCommand`'s ``__init__``.
- .. code-block:: python
- class Command(BaseCommand):
- def __init__(self, *args, **kwargs):
- super(Command, self).__init__(*args, **kwargs)
- # ...
- .. method:: BaseCommand.get_version()
- Return the Django version, which should be correct for all
- built-in Django commands. User-supplied commands can
- override this method to return their own version.
- .. method:: BaseCommand.execute(*args, **options)
- Try to execute this command, performing model validation if
- needed (as controlled by the attribute
- :attr:`requires_model_validation`). If the command raises a
- :class:`CommandError`, intercept it and print it sensibly to
- stderr.
- .. admonition:: Calling a management command in your code
- ``execute()`` should not be called directly from your code to execute a
- command. Use :ref:`call_command <call-command>` instead.
- .. method:: BaseCommand.handle(*args, **options)
- The actual logic of the command. Subclasses must implement this method.
- .. method:: BaseCommand.validate(app=None, display_num_errors=False)
- Validates the given app, raising :class:`CommandError` for any errors.
- If ``app`` is None, then all installed apps are validated.
- .. _ref-basecommand-subclasses:
- BaseCommand subclasses
- ----------------------
- .. class:: AppCommand
- A management command which takes one or more installed application
- names as arguments, and does something with each of them.
- Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement
- :meth:`~AppCommand.handle_app`, which will be called once for each application.
- .. method:: AppCommand.handle_app(app, **options)
- Perform the command's actions for ``app``, which will be the
- Python module corresponding to an application name given on
- the command line.
- .. class:: LabelCommand
- A management command which takes one or more arbitrary arguments
- (labels) on the command line, and does something with each of
- them.
- Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement
- :meth:`~LabelCommand.handle_label`, which will be called once for each label.
- .. method:: LabelCommand.handle_label(label, **options)
- Perform the command's actions for ``label``, which will be the
- string as given on the command line.
- .. class:: NoArgsCommand
- A command which takes no arguments on the command line.
- Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement
- :meth:`~NoArgsCommand.handle_noargs`; :meth:`~BaseCommand.handle` itself is
- overridden to ensure no arguments are passed to the command.
- .. method:: NoArgsCommand.handle_noargs(**options)
- Perform this command's actions
- .. _ref-command-exceptions:
- Command exceptions
- ------------------
- .. class:: CommandError
- Exception class indicating a problem while executing a management
- command.
- If this exception is raised during the execution of a management
- command from a command line console, it will be caught and turned into a
- nicely-printed error message to the appropriate output stream (i.e., stderr);
- as a result, raising this exception (with a sensible description of the
- error) is the preferred way to indicate that something has gone
- wrong in the execution of a command.
- If a management command is called from code through
- :ref:`call_command <call-command>`, it's up to you to catch the exception
- when needed.
|