Django's call_command fails with missing required arguments
Asked Answered
U

1

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)
Unequivocal answered 10/8, 2015 at 12:5 Comment(11)
show the code of your command (at least the option_list definitions)Prudence
Added the add_arguments function source.Unequivocal
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 ?Prudence
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-codeBinomial
I'm not calling with arg1, arg2, etc... obviously. I changed the question.Unequivocal
Ugh. You ever figure this out? Running into it myself.Lancer
Nope, ended up calling the Command object directly, bypassing argparse entirely.Unequivocal
Frustrating. I can't get it to work either, despite putting too much time into it already.Lancer
Calling the Command object directly is fairly simple. You should post another question about it, someone might be able to help you.Unequivocal
possible duplicate of call_command argument is requiredLancer
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.Lancer
B
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.

Binomial answered 16/8, 2015 at 17:17 Comment(5)
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.Unequivocal
argparse tries to be insensitive as to order.Binomial
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...?Unequivocal
Isn't url a flagged argument? Use '--url', '1', '--position', '2', ... or '--url=1', ...Binomial
This limitation should be fixed soonMassasauga

© 2022 - 2024 — McMap. All rights reserved.