5

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)
zmbq
  • 38,013
  • 14
  • 101
  • 171
  • 1
    show the code of your command (at least the `option_list` definitions) – Anentropic Aug 10 '15 at 12:43
  • Added the add_arguments function source. – zmbq Aug 10 '15 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` ? – Anentropic Aug 10 '15 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` – hpaulj Aug 10 '15 at 16:52
  • I'm not calling with arg1, arg2, etc... obviously. I changed the question. – zmbq Aug 11 '15 at 08:38
  • Ugh. You ever figure this out? Running into it myself. – mlissner Aug 16 '15 at 02:18
  • Nope, ended up calling the Command object directly, bypassing argparse entirely. – zmbq Aug 16 '15 at 07:13
  • Frustrating. I can't get it to work either, despite putting too much time into it already. – mlissner Aug 16 '15 at 15:00
  • Calling the Command object directly is fairly simple. You should post another question about it, someone might be able to help you. – zmbq Aug 16 '15 at 19:58
  • possible duplicate of [call\_command argument is required](http://stackoverflow.com/questions/32036562/call-command-argument-is-required) – mlissner Aug 17 '15 at 16:48
  • I went ahead and created a new question of my own, where I got a pretty good answer. I think mine is technically the dup, but I'm voting to close this one since mine has the answer. I have no idea if that's the correct thing to do on SO, but it should have the desired effect. – mlissner Aug 17 '15 at 16:49

1 Answers1

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.

Community
  • 1
  • 1
hpaulj
  • 221,503
  • 14
  • 230
  • 353
  • 2
    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. – zmbq Aug 16 '15 at 19:58
  • `argparse` tries to be insensitive as to order. – hpaulj Aug 16 '15 at 20:00
  • 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...? – zmbq Aug 16 '15 at 20:09
  • 1
    Isn't `url` a flagged argument? Use `'--url', '1', '--position', '2', ...` or `'--url=1', ...` – hpaulj Aug 16 '15 at 20:15
  • This limitation should [be fixed soon](https://code.djangoproject.com/ticket/29133) – atomic77 Feb 24 '18 at 01:12