Updated the AppCommand API to support apps without a models module.
This commit is contained in:
parent
aff57793b4
commit
bb8ec71f61
|
@ -7,6 +7,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import warnings
|
||||||
|
|
||||||
from optparse import make_option, OptionParser
|
from optparse import make_option, OptionParser
|
||||||
|
|
||||||
|
@ -112,8 +113,8 @@ class BaseCommand(object):
|
||||||
``args``
|
``args``
|
||||||
A string listing the arguments accepted by the command,
|
A string listing the arguments accepted by the command,
|
||||||
suitable for use in help messages; e.g., a command which takes
|
suitable for use in help messages; e.g., a command which takes
|
||||||
a list of application names might set this to '<appname
|
a list of application names might set this to '<app_label
|
||||||
appname ...>'.
|
app_label ...>'.
|
||||||
|
|
||||||
``can_import_settings``
|
``can_import_settings``
|
||||||
A boolean indicating whether the command needs to be able to
|
A boolean indicating whether the command needs to be able to
|
||||||
|
@ -331,19 +332,18 @@ class BaseCommand(object):
|
||||||
|
|
||||||
class AppCommand(BaseCommand):
|
class AppCommand(BaseCommand):
|
||||||
"""
|
"""
|
||||||
A management command which takes one or more installed application
|
A management command which takes one or more installed application labels
|
||||||
names as arguments, and does something with each of them.
|
as arguments, and does something with each of them.
|
||||||
|
|
||||||
Rather than implementing ``handle()``, subclasses must implement
|
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):
|
def handle(self, *app_labels, **options):
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
if not app_labels:
|
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
|
# Populate models and don't use only_with_models_module=True when
|
||||||
# calling get_app_config() to tell apart missing apps from apps
|
# calling get_app_config() to tell apart missing apps from apps
|
||||||
# without a model module -- which can't be supported with the legacy
|
# without a model module -- which can't be supported with the legacy
|
||||||
|
@ -355,23 +355,36 @@ class AppCommand(BaseCommand):
|
||||||
raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
|
raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
|
||||||
output = []
|
output = []
|
||||||
for app_config in app_configs:
|
for app_config in app_configs:
|
||||||
if app_config.models_module is None:
|
app_output = self.handle_app_config(app_config, **options)
|
||||||
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)
|
|
||||||
if app_output:
|
if app_output:
|
||||||
output.append(app_output)
|
output.append(app_output)
|
||||||
return '\n'.join(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
|
Perform the command's actions for app_config, an AppConfig instance
|
||||||
Python module corresponding to an application name given on
|
corresponding to an application label given on the command line.
|
||||||
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):
|
class LabelCommand(BaseCommand):
|
||||||
|
|
|
@ -313,17 +313,34 @@ BaseCommand subclasses
|
||||||
|
|
||||||
.. class:: AppCommand
|
.. class:: AppCommand
|
||||||
|
|
||||||
A management command which takes one or more installed application
|
A management command which takes one or more installed application labels as
|
||||||
names as arguments, and does something with each of them.
|
arguments, and does something with each of them.
|
||||||
|
|
||||||
Rather than implementing :meth:`~BaseCommand.handle`, subclasses must implement
|
Rather than implementing :meth:`~BaseCommand.handle`, subclasses must
|
||||||
:meth:`~AppCommand.handle_app`, which will be called once for each application.
|
implement :meth:`~AppCommand.handle_app_config`, which will be called once for
|
||||||
|
each application.
|
||||||
|
|
||||||
.. method:: AppCommand.handle_app(app, **options)
|
.. method:: AppCommand.handle_app_config(app_config, **options)
|
||||||
|
|
||||||
Perform the command's actions for ``app``, which will be the
|
Perform the command's actions for ``app_config``, which will be an
|
||||||
Python module corresponding to an application name given on
|
:class:`~django.apps.AppConfig` instance corresponding to an application
|
||||||
the command line.
|
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::
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
However, you may be able to simplify the implementation by using directly
|
||||||
|
the attributes of ``app_config``.
|
||||||
|
|
||||||
.. class:: LabelCommand
|
.. class:: LabelCommand
|
||||||
|
|
||||||
|
|
|
@ -179,6 +179,8 @@ these changes.
|
||||||
|
|
||||||
* The model and form ``IPAddressField`` will be removed.
|
* 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
|
* FastCGI support via the ``runfcgi`` management command will be
|
||||||
removed. Please deploy your project using WSGI.
|
removed. Please deploy your project using WSGI.
|
||||||
|
|
||||||
|
|
|
@ -593,6 +593,10 @@ methods are only referring to fields or other items in ``model._meta``.
|
||||||
App-loading changes
|
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
|
Since :setting:`INSTALLED_APPS` now supports application configuration classes
|
||||||
in addition to application modules, you should review code that accesses this
|
in addition to application modules, you should review code that accesses this
|
||||||
setting directly and use the app registry (:attr:`django.apps.apps`) instead.
|
setting directly and use the app registry (:attr:`django.apps.apps`) instead.
|
||||||
|
|
Loading…
Reference in New Issue