mirror of
				https://github.com/django/django.git
				synced 2025-11-04 05:35:37 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			375 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Text
		
	
	
	
	
	
			
		
		
	
	
			375 lines
		
	
	
	
		
			14 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::
 | 
						||
 | 
						||
    from django.core.management.base import BaseCommand, CommandError
 | 
						||
    from polls.models import Question as Poll
 | 
						||
 | 
						||
    class Command(BaseCommand):
 | 
						||
        help = 'Closes the specified poll for voting'
 | 
						||
 | 
						||
        def add_arguments(self, parser):
 | 
						||
            parser.add_argument('poll_id', nargs='+', type=int)
 | 
						||
 | 
						||
        def handle(self, *args, **options):
 | 
						||
            for poll_id in options['poll_id']:
 | 
						||
                try:
 | 
						||
                    poll = Poll.objects.get(pk=poll_id)
 | 
						||
                except Poll.DoesNotExist:
 | 
						||
                    raise CommandError('Poll "%s" does not exist' % poll_id)
 | 
						||
 | 
						||
                poll.opened = False
 | 
						||
                poll.save()
 | 
						||
 | 
						||
                self.stdout.write(self.style.SUCCESS('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 one or more ``poll_ids`` and sets ``poll.opened``
 | 
						||
to ``False`` for each one. If the user referenced any nonexistent polls, a
 | 
						||
:exc:`CommandError` is raised. The ``poll.opened`` attribute does not exist in
 | 
						||
the :doc:`tutorial</intro/tutorial01>` and was added to
 | 
						||
``polls.models.Question`` for this example.
 | 
						||
 | 
						||
.. _custom-commands-options:
 | 
						||
 | 
						||
Accepting optional arguments
 | 
						||
============================
 | 
						||
 | 
						||
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 can be added in the :meth:`~BaseCommand.add_arguments` method like this::
 | 
						||
 | 
						||
    class Command(BaseCommand):
 | 
						||
        def add_arguments(self, parser):
 | 
						||
            # Positional arguments
 | 
						||
            parser.add_argument('poll_id', nargs='+', type=int)
 | 
						||
 | 
						||
            # Named (optional) arguments
 | 
						||
            parser.add_argument(
 | 
						||
                '--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:`argparse` Python documentation
 | 
						||
for more about ``add_argument`` 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 :option:`--verbosity` and :option:`--traceback`.
 | 
						||
 | 
						||
.. _management-commands-and-locales:
 | 
						||
 | 
						||
Management commands and locales
 | 
						||
===============================
 | 
						||
 | 
						||
By default, the :meth:`BaseCommand.execute` method deactivates translations
 | 
						||
because some commands shipped with Django perform several tasks (for example,
 | 
						||
user-facing content rendering and database population) that require a
 | 
						||
project-neutral string language.
 | 
						||
 | 
						||
If, for some reason, your custom management command needs to use a fixed locale,
 | 
						||
you should manually activate and deactivate it in your
 | 
						||
:meth:`~BaseCommand.handle` method using the functions provided by the I18N
 | 
						||
support code::
 | 
						||
 | 
						||
    from django.core.management.base import BaseCommand, CommandError
 | 
						||
    from django.utils import translation
 | 
						||
 | 
						||
    class Command(BaseCommand):
 | 
						||
        ...
 | 
						||
 | 
						||
        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 deactivating it. 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
 | 
						||
  deactivating translations).
 | 
						||
 | 
						||
* 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.
 | 
						||
 | 
						||
Testing
 | 
						||
=======
 | 
						||
 | 
						||
Information on how to test custom management commands can be found in the
 | 
						||
:ref:`testing docs <topics-testing-management-commands>`.
 | 
						||
 | 
						||
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.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.missing_args_message
 | 
						||
 | 
						||
    If your command defines mandatory positional arguments, you can customize
 | 
						||
    the message error returned in the case of missing arguments. The default is
 | 
						||
    output by :py:mod:`argparse` ("too few 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_migrations_checks
 | 
						||
 | 
						||
    A boolean; if ``True``, the command prints a warning if the set of
 | 
						||
    migrations on disk don't match the migrations in the database. A warning
 | 
						||
    doesn't prevent the command from executing. Default value is ``False``.
 | 
						||
 | 
						||
.. attribute:: BaseCommand.requires_system_checks
 | 
						||
 | 
						||
    A boolean; if ``True``, the entire Django project will be checked for
 | 
						||
    potential problems prior to executing the command. Default value is ``True``.
 | 
						||
 | 
						||
.. 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 :mod:`django.contrib.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.
 | 
						||
 | 
						||
.. attribute:: BaseCommand.style
 | 
						||
 | 
						||
    An instance attribute that helps create colored output when writing to
 | 
						||
    ``stdout`` or ``stderr``. For example::
 | 
						||
 | 
						||
        self.stdout.write(self.style.SUCCESS('...'))
 | 
						||
 | 
						||
    See :ref:`syntax-coloring` to learn how to modify the color palette and to
 | 
						||
    see the available styles (use uppercased versions of the "roles" described
 | 
						||
    in that section).
 | 
						||
 | 
						||
    If you pass the :option:`--no-color` option when running your command, all
 | 
						||
    ``self.style()`` calls will return the original string uncolored.
 | 
						||
 | 
						||
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__``::
 | 
						||
 | 
						||
        class Command(BaseCommand):
 | 
						||
            def __init__(self, *args, **kwargs):
 | 
						||
                super(Command, self).__init__(*args, **kwargs)
 | 
						||
                # ...
 | 
						||
 | 
						||
.. method:: BaseCommand.add_arguments(parser)
 | 
						||
 | 
						||
    Entry point to add parser arguments to handle command line arguments passed
 | 
						||
    to the command. Custom commands should override this method to add both
 | 
						||
    positional and optional arguments accepted by the command. Calling
 | 
						||
    ``super()`` is not needed when directly subclassing ``BaseCommand``.
 | 
						||
 | 
						||
.. method:: BaseCommand.get_version()
 | 
						||
 | 
						||
    Returns 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)
 | 
						||
 | 
						||
    Tries to execute this command, performing system checks if needed (as
 | 
						||
    controlled by the :attr:`requires_system_checks` attribute). If the command
 | 
						||
    raises a :exc:`CommandError`, it's intercepted and printed to stderr.
 | 
						||
 | 
						||
.. admonition:: Calling a management command in your code
 | 
						||
 | 
						||
    ``execute()`` should not be called directly from your code to execute a
 | 
						||
    command. Use :func:`~django.core.management.call_command` instead.
 | 
						||
 | 
						||
.. method:: BaseCommand.handle(*args, **options)
 | 
						||
 | 
						||
    The actual logic of the command. Subclasses must implement this method.
 | 
						||
 | 
						||
    It may return a Unicode string which will be printed to ``stdout`` (wrapped
 | 
						||
    by ``BEGIN;`` and ``COMMIT;`` if :attr:`output_transaction` is ``True``).
 | 
						||
 | 
						||
.. method:: BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)
 | 
						||
 | 
						||
    Uses the system check framework to inspect the entire Django project for
 | 
						||
    potential problems. Serious problems are raised as a :exc:`CommandError`;
 | 
						||
    warnings are output to stderr; minor notifications are output to stdout.
 | 
						||
 | 
						||
    If ``app_configs`` and ``tags`` are both ``None``, all system checks are
 | 
						||
    performed. ``tags`` can be a list of check tags, like ``compatibility`` or
 | 
						||
    ``models``.
 | 
						||
 | 
						||
.. _ref-basecommand-subclasses:
 | 
						||
 | 
						||
``BaseCommand`` subclasses
 | 
						||
--------------------------
 | 
						||
 | 
						||
.. class:: AppCommand
 | 
						||
 | 
						||
A management command which takes one or more installed application labels as
 | 
						||
arguments, and does something with each of them.
 | 
						||
 | 
						||
Rather than implementing :meth:`~BaseCommand.handle`, subclasses must
 | 
						||
implement :meth:`~AppCommand.handle_app_config`, which will be called once for
 | 
						||
each application.
 | 
						||
 | 
						||
.. method:: AppCommand.handle_app_config(app_config, **options)
 | 
						||
 | 
						||
    Perform the command's actions for ``app_config``, which will be an
 | 
						||
    :class:`~django.apps.AppConfig` instance corresponding to an application
 | 
						||
    label 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.
 | 
						||
 | 
						||
.. attribute:: LabelCommand.label
 | 
						||
 | 
						||
    A string describing the arbitrary arguments passed to the command. The
 | 
						||
    string is used in the usage text and error messages of the command.
 | 
						||
    Defaults to ``'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.
 | 
						||
 | 
						||
Command exceptions
 | 
						||
------------------
 | 
						||
 | 
						||
.. exception:: 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
 | 
						||
:func:`~django.core.management.call_command`, it's up to you to catch the
 | 
						||
exception when needed.
 |