summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2014-08-09 21:03:19 +0200
committerClaude Paroz <claude@2xlibre.net>2014-08-12 21:53:29 +0200
commit2cc8ffe258008096a70791115b2daa12f0ef0192 (patch)
tree8d30f9ed8fc1c1931d35ecc5410f164a1171f3eb
parent8f9862cd4d0e9706babf947079739fd476455df7 (diff)
Fixed #22985 -- Made call_command accept option name parameter
Thanks giulettamasina for the report and Tim Graham for the review.
-rw-r--r--django/core/management/__init__.py6
-rw-r--r--docs/ref/django-admin.txt19
-rw-r--r--docs/releases/1.8.txt7
-rw-r--r--tests/user_commands/management/commands/dance.py2
-rw-r--r--tests/user_commands/tests.py20
5 files changed, 48 insertions, 6 deletions
diff --git a/django/core/management/__init__.py b/django/core/management/__init__.py
index 75cd48461c..d74befa844 100644
--- a/django/core/management/__init__.py
+++ b/django/core/management/__init__.py
@@ -102,8 +102,12 @@ def call_command(name, *args, **options):
# Simulate argument parsing to get the option defaults (see #10080 for details).
parser = command.create_parser('', name)
if command.use_argparse:
+ # Use the `dest` option name from the parser option
+ opt_mapping = dict((sorted(s_opt.option_strings)[0].lstrip('-').replace('-', '_'), s_opt.dest)
+ for s_opt in parser._actions if s_opt.option_strings)
+ arg_options = dict((opt_mapping.get(key, key), value) for key, value in options.items())
defaults = parser.parse_args(args=args)
- defaults = dict(defaults._get_kwargs(), **options)
+ defaults = dict(defaults._get_kwargs(), **arg_options)
else:
# Legacy optparse method
defaults, _ = parser.parse_args(args=[])
diff --git a/docs/ref/django-admin.txt b/docs/ref/django-admin.txt
index d271eafc00..85d0ab06fe 100644
--- a/docs/ref/django-admin.txt
+++ b/docs/ref/django-admin.txt
@@ -1824,10 +1824,27 @@ Examples::
management.call_command('loaddata', 'test_data', verbosity=0)
Note that command options that take no arguments are passed as keywords
-with ``True`` or ``False``::
+with ``True`` or ``False``, as you can see with the ``interactive`` option above.
+Named arguments can be passed by using either one of the following syntaxes::
+
+ # Similar to the command line
+ management.call_command('dumpdata', '--natural')
+
+ # Named argument similar to the command line minus the initial dashes and
+ # with internal dashes replaced by underscores
+ management.call_command('dumpdata', natural=True)
+
+ # `use_natural_keys` is the option destination variable
management.call_command('dumpdata', use_natural_keys=True)
+.. versionchanged:: 1.8
+
+ The first syntax is now supported thanks to management commands using the
+ :py:mod:`argparse` module. For the second syntax, Django previously passed
+ the option name as-is to the command, now it is always using the ``dest``
+ variable name (which may or may not be the same as the option name).
+
Command options which take multiple options are passed a list::
management.call_command('dumpdata', exclude=['contenttypes', 'auth'])
diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt
index 318b6ce49f..86d39c4f88 100644
--- a/docs/releases/1.8.txt
+++ b/docs/releases/1.8.txt
@@ -196,6 +196,13 @@ Management Commands
* :djadmin:`inspectdb` now outputs ``Meta.unique_together``.
+* When calling management commands from code through :ref:`call_command
+ <call-command>` and passing options, the option name can match the command
+ line option name (without the initial dashes) or the final option destination
+ variable name, but in either case, the resulting option received by the
+ command is now always the ``dest`` name specified in the command option
+ definition (as long as the command uses the new :py:mod:`argparse` module).
+
Models
^^^^^^
diff --git a/tests/user_commands/management/commands/dance.py b/tests/user_commands/management/commands/dance.py
index 0c47401c25..e27042f9d4 100644
--- a/tests/user_commands/management/commands/dance.py
+++ b/tests/user_commands/management/commands/dance.py
@@ -9,9 +9,11 @@ class Command(BaseCommand):
def add_arguments(self, parser):
parser.add_argument("-s", "--style", default="Rock'n'Roll")
parser.add_argument("-x", "--example")
+ parser.add_argument("--opt-3", action='store_true', dest='option3')
def handle(self, *args, **options):
example = options["example"]
if example == "raise":
raise CommandError()
self.stdout.write("I don't feel like dancing %s." % options["style"])
+ self.stdout.write(','.join(options.keys()))
diff --git a/tests/user_commands/tests.py b/tests/user_commands/tests.py
index ac2e51c807..398e01eb6b 100644
--- a/tests/user_commands/tests.py
+++ b/tests/user_commands/tests.py
@@ -15,14 +15,15 @@ class CommandTests(SimpleTestCase):
def test_command(self):
out = StringIO()
management.call_command('dance', stdout=out)
- self.assertEqual(out.getvalue(),
- "I don't feel like dancing Rock'n'Roll.\n")
+ self.assertIn("I don't feel like dancing Rock'n'Roll.\n", out.getvalue())
def test_command_style(self):
out = StringIO()
management.call_command('dance', style='Jive', stdout=out)
- self.assertEqual(out.getvalue(),
- "I don't feel like dancing Jive.\n")
+ self.assertIn("I don't feel like dancing Jive.\n", out.getvalue())
+ # Passing options as arguments also works (thanks argparse)
+ management.call_command('dance', '--style', 'Jive', stdout=out)
+ self.assertIn("I don't feel like dancing Jive.\n", out.getvalue())
def test_language_preserved(self):
out = StringIO()
@@ -76,6 +77,17 @@ class CommandTests(SimpleTestCase):
if current_path is not None:
os.environ['PATH'] = current_path
+ def test_call_command_option_parsing(self):
+ """
+ When passing the long option name to call_command, the available option
+ key is the option dest name (#22985).
+ """
+ out = StringIO()
+ management.call_command('dance', stdout=out, opt_3=True)
+ self.assertIn("option3", out.getvalue())
+ self.assertNotIn("opt_3", out.getvalue())
+ self.assertNotIn("opt-3", out.getvalue())
+
def test_optparse_compatibility(self):
"""
optparse should be supported during Django 1.8/1.9 releases.