Fixed #22985 -- Made call_command accept option name parameter
Thanks giulettamasina for the report and Tim Graham for the review.
This commit is contained in:
parent
8f9862cd4d
commit
2cc8ffe258
|
@ -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=[])
|
||||
|
|
|
@ -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'])
|
||||
|
|
|
@ -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
|
||||
^^^^^^
|
||||
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue