аргумент call_command обязателен

Я пытаюсь использовать Django call_command способом, очень похожим на этот вопрос без ответа.

Как я это называю:

    args = []
    kwargs = {
        'solr_url': 'http://127.0.0.1:8983/solr/collection1',
        'type': 'opinions',
        'update': True,
        'everything': True,
        'do_commit': True,
        'traceback': True,
    }
    call_command('cl_update_index', **kwargs)

Теоретически это должно работать, согласно документы. Но это не работает, просто не работает.

Вот метод add_arguments для моего класса Command:

def add_arguments(self, parser):
    parser.add_argument(
        '--type',
        type=valid_obj_type,
        required=True,
        help='Because the Solr indexes are loosely bound to the database, '
             'commands require that the correct model is provided in this '
             'argument. Current choices are "audio" or "opinions".'
    )
    parser.add_argument(
        '--solr-url',
        required=True,
        type=str,
        help='When swapping cores, it can be valuable to use a temporary '
             'Solr URL, overriding the default value that\'s in the '
             'settings, e.g., http://127.0.0.1:8983/solr/swap_core'
    )

    actions_group = parser.add_mutually_exclusive_group()
    actions_group.add_argument(
        '--update',
        action='store_true',
        default=False,
        help='Run the command in update mode. Use this to add or update '
             'items.'
    )
    actions_group.add_argument(
        '--delete',
        action='store_true',
        default=False,
        help='Run the command in delete mode. Use this to remove  items '
             'from the index. Note that this will not delete items from '
             'the index that do not continue to exist in the database.'
    )
    parser.add_argument(
        '--optimize',
        action='store_true',
        default=False,
        help='Run the optimize command against the current index after '
             'any updates or deletions are completed.'
    )
    parser.add_argument(
        '--do-commit',
        action='store_true',
        default=False,
        help='Performs a simple commit and nothing more.'
    )

    act_upon_group = parser.add_mutually_exclusive_group()
    act_upon_group.add_argument(
        '--everything',
        action='store_true',
        default=False,
        help='Take action on everything in the database',
    )
    act_upon_group.add_argument(
        '--query',
        help='Take action on items fulfilling a query. Queries should be '
             'formatted as Python dicts such as: "{\'court_id\':\'haw\'}"'
    )
    act_upon_group.add_argument(
        '--items',
        type=int,
        nargs='*',
        help='Take action on a list of items using a single '
             'Celery task'
    )
    act_upon_group.add_argument(
        '--datetime',
        type=valid_date_time,
        help='Take action on items newer than a date (YYYY-MM-DD) or a '
             'date and time (YYYY-MM-DD HH:MM:SS)'
    )

Что бы я здесь ни делал, я получаю:

CommandError: Ошибка: требуется аргумент --type

Любые идеи? Если вам действительно интересно, вы можете см. весь код здесь.


person mlissner    schedule 16.08.2015    source источник
comment
action=store_true и default=False противоречат друг другу. store_true это псевдоним для store_const с const=True   -  person Incognos    schedule 16.08.2015


Ответы (1)


Вы определили аргумент с флагом '--type' и сделали его required. Для этой командной строки потребуется строка или строки, которые выглядят как --type avalue.

Это похоже на соответствующую часть call_command:

def call_command(name, *args, **options):
    ....
    parser = command.create_parser('', name)
    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)
        defaults = dict(defaults._get_kwargs(), **arg_options)
        # Move positional args out of options to mimic legacy optparse
        args = defaults.pop('args', ())

Он создает синтаксический анализатор, используя свои собственные аргументы, а также те, которые вы добавляете.

parser._actions if s_opt.option_strings - это аргументы (Действия), которые принимают флаг опции (начинаются с - или --). opt_mapping — это сопоставление между строками флагов (минус начальные -s) и атрибутом «dest».

arg_options преобразует ваш **kwargs во что-то, что можно объединить с выводом parser.

defaults = parser.parse_args(args=args) выполняет фактический синтаксический анализ. То есть это единственный код, который фактически использует механизм разбора argparse. Таким образом, часть *args вашего звонка имитирует создание sys.argv[1:] из интерактивного звонка.

Основываясь на этом чтении, я думаю, что это должно работать:

args = [
    '--solr-url', 'http://127.0.0.1:8983/solr/collection1',
    '--type', 'opinions',
    '--update'
    '--everything',
    '--do_commit',
    '--traceback',
}
call_command('cl_update_index', *args)

Вместо **kwargs я передаю значения в виде списка строк. Или два обязательных аргумента можно передать в args, а остальные — в **kwargs.

args = ['--solr-url', 'http://127.0.0.1:8983/solr/collection1',
    '--type', 'opinions']
kwargs = {
    'update': True,
    'everything': True,
    'do_commit': True,
    'traceback': True,
}
call_command('cl_update_index', *args, **kwargs)

Если аргумент равен required, его необходимо передать через *args. **kwargs обходит синтаксический анализатор, заставляя его возражать против отсутствующих аргументов.


Я скачал последнюю версию django, но не устанавливал ее. Но вот симуляция call_command, которая должна проверить варианты вызова:

import argparse

def call_command(name, *args, **options):
    """
    Calls the given command, with the given options and args/kwargs.
    standalone simulation of django.core.mangement call_command
    """
    command = name
    """
    ....
    """
    # 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 = {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)
        defaults = dict(defaults._get_kwargs(), **arg_options)
        # Move positional args out of options to mimic legacy optparse
        args = defaults.pop('args', ())
    else:
        # Legacy optparse method
        defaults, _ = parser.parse_args(args=[])
        defaults = dict(defaults.__dict__, **options)
    if 'skip_checks' not in options:
        defaults['skip_checks'] = True

    return command.execute(*args, **defaults)

class BaseCommand():
    def __init__(self):
        self.use_argparse = True
        self.stdout= sys.stdout
        self.stderr=sys.stderr
    def execute(self, *args, **kwargs):
        self.handle(*args, **kwargs)
    def handle(self, *args, **kwargs):
        print('args: ', args)
        print('kwargs: ', kwargs)
    def create_parser(self, *args, **kwargs):
        parser = argparse.ArgumentParser()
        self.add_arguments(parser)
        return parser
    def add_arguments(self, parser):
        parser.add_argument('--type', required=True)
        parser.add_argument('--update', action='store_true')
        parser.add_argument('--optional', default='default')
        parser.add_argument('foo')
        parser.add_argument('args', nargs='*')

if __name__=='__main__':

    testcmd = BaseCommand()
    # testcmd.execute('one','tow', three='four')

    call_command(testcmd, '--type','typevalue','foovalue', 'argsvalue', update=True)

    args = ['--type=argvalue', 'foovalue', '1', '2']
    kwargs = {
        'solr_url': 'http://127.0.0.1...',
        'type': 'opinions',
        'update': True,
        'everything': True,
    }
    call_command(testcmd, *args, **kwargs)

который производит:

python3 stack32036562.py 
args:  ('argsvalue',)
kwargs:  {'optional': 'default', 'type': 'typevalue', 'update': True, 'skip_checks': True, 'foo': 'foovalue'}
args:  ('1', '2')
kwargs:  {'optional': 'default', 'update': True, 'foo': 'foovalue', 'type': 'opinions', 'skip_checks': True, 'everything': True, 'solr_url': 'http://127.0.0.1...'}

С кучей заглушек я могу заставить ваш cl Command работать с моим BaseCommand, и работает следующий вызов:

clupdate = Command()
args = ['--type','opinions','--solr-url','dummy']
kwargs = {
    'solr_url': 'http://127.0.0.1:8983/solr/collection1',
    #'type': 'opinions',
    'update': True,
    'everything': True,
    'do_commit': True,
    'traceback': True,
}
call_command(clupdate, *args, **kwargs)

выполнение заглушки everything.

Running in update mode...
everything
args:  ()
options:  {'type': 'opinions', 'query': None, 'solr_url': 'http://127.0.0.1:8983/solr/collection1', 'items': None, 'do_commit': True, 'update': True, 'delete': False, 'datetime': None, 'optimize': False, 'skip_checks': True, 'everything': True, 'traceback': True}
person hpaulj    schedule 16.08.2015
comment
Основываясь на чтении кода django, я думаю, что аргументы required нужно передавать через *args, а не **kwargs. - person hpaulj; 16.08.2015
comment
Отличный ответ, хотя я все еще не мог ничего передать как диктат. Даже то, что вы сказали, должно работать? Не работает. Единственный способ, которым это работает для меня, - передать все в виде списка. - person mlissner; 17.08.2015
comment
Я не устанавливал последнюю версию django, но пробовал имитировать call_command. Как и ожидалось, kwargs переходит к команде с минимальными изменениями. - person hpaulj; 17.08.2015
comment
Интересно. Я на Джанго 1.8.3. Не уверен, в чем разница, но я достаточно счастлив просто использовать аргументы и назвать это достаточно хорошим! - person mlissner; 18.08.2015