Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Updated the AppCommand API to support apps without a models module.

  • Loading branch information...
commit bb8ec71f61066587aa3a8feb0aedab213422775a 1 parent aff5779
@aaugustin aaugustin authored
View
51 django/core/management/base.py
@@ -7,6 +7,7 @@
import os
import sys
+import warnings
from optparse import make_option, OptionParser
@@ -112,8 +113,8 @@ class BaseCommand(object):
``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 ...>'.
+ a list of application names might set this to '<app_label
+ app_label ...>'.
``can_import_settings``
A boolean indicating whether the command needs to be able to
@@ -331,19 +332,18 @@ def handle(self, *args, **options):
class AppCommand(BaseCommand):
"""
- A management command which takes one or more installed application
- names as arguments, and does something with each of them.
+ A management command which takes one or more installed application labels
+ as arguments, and does something with each of them.
Rather than implementing ``handle()``, subclasses must implement
- ``handle_app()``, which will be called once for each application.
-
+ ``handle_app_config()``, which will be called once for each application.
"""
- args = '<appname appname ...>'
+ args = '<app_label app_label ...>'
def handle(self, *app_labels, **options):
from django.apps import apps
if not app_labels:
- raise CommandError('Enter at least one appname.')
+ raise CommandError("Enter at least one application label.")
# Populate models and don't use only_with_models_module=True when
# calling get_app_config() to tell apart missing apps from apps
# without a model module -- which can't be supported with the legacy
@@ -355,23 +355,36 @@ def handle(self, *app_labels, **options):
raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
output = []
for app_config in app_configs:
- if app_config.models_module is None:
- raise CommandError(
- "AppCommand cannot handle app %r because it doesn't have "
- "a models module." % app_config.label)
- app_output = self.handle_app(app_config.models_module, **options)
+ app_output = self.handle_app_config(app_config, **options)
if app_output:
output.append(app_output)
return '\n'.join(output)
- def handle_app(self, app, **options):
+ def handle_app_config(self, app_config, **options):
"""
- Perform the command's actions for ``app``, which will be the
- Python module corresponding to an application name given on
- the command line.
-
+ Perform the command's actions for app_config, an AppConfig instance
+ corresponding to an application label given on the command line.
"""
- raise NotImplementedError('subclasses of AppCommand must provide a handle_app() method')
+ try:
+ # During the deprecation path, keep delegating to handle_app if
+ # handle_app_config isn't implemented in a subclass.
+ handle_app = self.handle_app
+ except AttributeError:
+ # Keep only this exception when the deprecation completes.
+ raise NotImplementedError(
+ "Subclasses of AppCommand must provide"
+ "a handle_app_config() method.")
+ else:
+ warnings.warn(
+ "AppCommand.handle_app() is superseded by "
+ "AppCommand.handle_app_config().",
+ PendingDeprecationWarning, stacklevel=2)
+ if app_config.models_module is None:
+ raise CommandError(
+ "AppCommand cannot handle app '%s' in legacy mode "
+ "because it doesn't have a models module."
+ % app_config.label)
+ return handle_app(app_config.models_module, **options)
class LabelCommand(BaseCommand):
View
33 docs/howto/custom-management-commands.txt
@@ -313,17 +313,34 @@ BaseCommand subclasses
.. class:: AppCommand
-A management command which takes one or more installed application
-names as arguments, and does something with each of them.
+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`, which will be called once for each application.
+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.
+
+.. versionchanged:: 1.7
+
+ Previously, :class:`AppCommand` subclasses had to implement
+ ``handle_app(app, **options)`` where ``app`` was a models module. The new
+ API makes it possible to handle applications without a models module. The
+ fastest way to migrate is as follows::
-.. method:: AppCommand.handle_app(app, **options)
+ def handle_app_config(app_config, **options):
+ if app_config.models_module is None:
+ return # Or raise an exception.
+ app = app_config.models_module
+ # Copy the implementation of handle_app(app_config, **options) here.
- Perform the command's actions for ``app``, which will be the
- Python module corresponding to an application name given on
- the command line.
+ However, you may be able to simplify the implementation by using directly
+ the attributes of ``app_config``.
.. class:: LabelCommand
View
2  docs/internals/deprecation.txt
@@ -179,6 +179,8 @@ these changes.
* The model and form ``IPAddressField`` will be removed.
+* ``AppCommand.handle_app()`` will no longer be supported.
+
* FastCGI support via the ``runfcgi`` management command will be
removed. Please deploy your project using WSGI.
View
4 docs/releases/1.7.txt
@@ -593,6 +593,10 @@ methods are only referring to fields or other items in ``model._meta``.
App-loading changes
~~~~~~~~~~~~~~~~~~~
+Subclasses of :class:`~django.core.management.AppCommand` must now implement a
+:meth:`~django.core.management.AppCommand.handle_app_config` method instead of
+``handle_app()``. This method receives an :class:`~django.apps.AppConfig` instance.
+
Since :setting:`INSTALLED_APPS` now supports application configuration classes
in addition to application modules, you should review code that accesses this
setting directly and use the app registry (:attr:`django.apps.apps`) instead.
Please sign in to comment.
Something went wrong with that request. Please try again.