6

I want to call a Django management command from one of my tests. I'm using django.core.management.call_command for this. And it doesn't work.

I have a command with 4 required arguments. When I call it, it complains all arguments are missing even though I'm passing them:

call_command('my_command', url='12', project='abc', website='zbb', title='12345')

I get the base command error that --url, --project, --website and --title are missing.

I did not specify a different destination for these arguments.

I looked at the call_command source and pinpointed the problem to the following line in call_command:

if command.use_argparse:
    # Use the `dest` option name from the parser option
    opt_mapping = {sorted(s_opt.option_strings)[0].lstrip('-').replace('-', '_'): s_opt.dest
                   for s_opt in parser._actions if s_opt.option_strings}
    arg_options = {opt_mapping.get(key, key): value for key, value in options.items()}
    defaults = parser.parse_args(args=args)    ****** THIS *****
    defaults = dict(defaults._get_kwargs(), **arg_options)
    # Move positional args out of options to mimic legacy optparse
    args = defaults.pop('args', ())

args is the positional arguments passed to call_commands, which is empty. I'm only passing named arguments. parser.parse_args complains the required variables are missing.

This is in Django 1.8.3.

Here is my command's add_arguments function (I just removed the help strings for brevity):

def add_arguments(self, parser):
    parser.add_argument('--url', action='store', required=True)
    parser.add_argument('--project', action='store', required=True)
    parser.add_argument('--continue-processing', action='store_true', default=False)
    parser.add_argument('--website', action='store', required=True)
    parser.add_argument('--title', action='store', required=True)
    parser.add_argument('--duplicate', action='store_true',default=False)
11
  • 1
    show the code of your command (at least the option_list definitions) Commented Aug 10, 2015 at 12:43
  • Added the add_arguments function source. Commented Aug 10, 2015 at 12:47
  • are you really calling it with arg1='12', arg2='abc', arg3='zbb', arg4='12345' keyword args? are you aware that for option parser.add_argument('--url') the keyword arg should be url ? Commented Aug 10, 2015 at 13:15
  • Where do you define arg1, arg2, etc as argumets? Your call does not look like the examples in https://docs.djangoproject.com/en/1.8/ref/django-admin/#running-management-commands-from-your-code Commented Aug 10, 2015 at 16:52
  • I'm not calling with arg1, arg2, etc... obviously. I changed the question. Commented Aug 11, 2015 at 8:38

1 Answer 1

3

Based on that piece of code which you posted, I've concluded in call_command argument is required

that the required named arguments have to be passed in through *args, not just the positionals.

**kwargs bypasses the parser. So it doesn't see anything you defined there. **kwargs may override the *args values, but *args still needs something for each required argument. If you don't want to do that, then turn off the required attribute.

Sign up to request clarification or add additional context in comments.

5 Comments

Yes, I have seen that in call_command's code, as well. It's a bug in call_command. I can't pass the required arguments in *args - what is their right order? Maybe the order I called add_argument, maybe something else, maybe it depends on the OS or Python version. I don't want to risk it.
argparse tries to be insensitive as to order.
How can it possibly be insensitive? If I put '1', '2', '3', '4' in args, how can it tell which one is url, which one is project, etc...?
Isn't url a flagged argument? Use '--url', '1', '--position', '2', ... or '--url=1', ...
This limitation should be fixed soon

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.