| ==================================== |
| 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) |
| |
| .. _management-commands-output: |
| |
| .. 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. Note also that you don't need to end messages with a newline |
| character, it will be added automatically, unless you specify the ``ending`` |
| parameter:: |
| |
| self.stdout.write("Unterminated line", ending='') |
| |
| 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. |