Skip to content
Browse files

Fixed #22985 -- Made call_command accept option name parameter

Thanks giulettamasina for the report and Tim Graham for the review.
  • Loading branch information...
1 parent 8f9862c commit 2cc8ffe258008096a70791115b2daa12f0ef0192 @claudep claudep committed
6 django/core/management/
@@ -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)
# Legacy optparse method
defaults, _ = parser.parse_args(args=[])
19 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'])
7 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).
2 tests/user_commands/management/commands/
@@ -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()))
20 tests/user_commands/
@@ -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 @@ def test_find_command_without_PATH(self):
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.

0 comments on commit 2cc8ffe

Please sign in to comment.
Something went wrong with that request. Please try again.