mirror of
				https://github.com/django/django.git
				synced 2025-10-30 11:37:19 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			373 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			373 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
| ====================================
 | |
| 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`.
 | |
| 
 | |
| .. _management-commands-and-locales:
 | |
| 
 | |
| Management commands and locales
 | |
| ===============================
 | |
| 
 | |
| By default, the :meth:`BaseCommand.execute` method sets the hardcoded 'en-us'
 | |
| locale because some 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, for some reason, your custom management command needs to use a fixed locale
 | |
| different from 'en-us', 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()
 | |
| 
 | |
| Another need might be that your command simply should use the locale set in
 | |
| settings and Django should be kept from forcing it to 'en-us'. You can achieve
 | |
| it by using the :data:`BaseCommand.leave_locale_alone` option.
 | |
| 
 | |
| When working on the scenarios described above though, take into account that
 | |
| system management commands typically have to be very careful about running in
 | |
| non-uniform locales, so you might need to:
 | |
| 
 | |
| * Make sure the :setting:`USE_I18N` setting is always ``True`` when running
 | |
|   the command (this is a 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`.
 | |
| 
 | |
| .. attribute:: BaseCommand.leave_locale_alone
 | |
| 
 | |
|   A boolean indicating whether the locale set in settings should be preserved
 | |
|   during the execution of the command instead of being forcibly set to 'en-us'.
 | |
| 
 | |
|   Default value is ``False``.
 | |
| 
 | |
|   Make sure you know what you are doing if you decide to change the value of
 | |
|   this option in your custom command if it creates database content that
 | |
|   is locale-sensitive and such content shouldn't contain any translations (like
 | |
|   it happens e.g. with django.contrim.auth permissions) as making the locale
 | |
|   differ from the de facto default 'en-us' might cause unintended effects. See
 | |
|   the `Management commands and locales`_ section above for further details.
 | |
| 
 | |
|   This option can't be ``False`` when the
 | |
|   :data:`~BaseCommand.can_import_settings` option is set to ``False`` too
 | |
|   because attempting to set the locale needs access to settings. This condition
 | |
|   will generate a :class:`CommandError`.
 | |
| 
 | |
| .. versionadded:: 1.6
 | |
| 
 | |
| The ``leave_locale_alone`` option was added in Django 1.6.
 | |
| 
 | |
| 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.
 | 
