2009-12-21 2 views
20

я получаю следующее сообщение об ошибке при создании экземпляра формы Django с конструктором перекрываться:ошибка Джанго: получил несколько значений аргумента ключевого слова

__init__() got multiple values for keyword argument 'collection_type' 

The __init__() функции (как показано ниже) точно так, как написано это, но с # code заменил мою логику. Имейте в виду, что я по существу переопределяет конструктор формы (который является ModelForm).

def __init__(self, collection_type, user=None, parent=None, *args, **kwargs): 
    # code 
    super(self.__class__, self).__init__(*args, **kwargs) 

Вызов, который создает ошибку показано здесь:

form = CreateCollectionForm(
    request.POST, 
    collection_type=collection_type, 
    parent=parent, 
    user=request.user 
) 

Я не вижу никаких причин, почему ошибка выскакивает.

EDIT: Вот полный код для конструктора

def __init__(self, collection_type, user=None, parent=None, *args, **kwargs): 
    self.collection_type = collection_type 
    if self.collection_type == 'library': 
     self.user = user 
    elif self.collection_type == 'bookshelf' or self.collection_type == 'series': 
     self.parent = parent 
    else: 
     raise AssertionError, 'collection_type must be "library", "bookshelf" or "series"' 
    super(self.__class__, self).__init__(*args, **kwargs) 

EDIT: StackTrace

Environment: 

Request Method: POST 
Request URL: http://localhost:8000/forms/create_bookshelf/hello 
Django Version: 1.1.1 
Python Version: 2.6.1 
Installed Applications: 
['django.contrib.auth', 
'django.contrib.contenttypes', 
'django.contrib.sessions', 
'django.contrib.sites', 
'libraries', 
'users', 
'books', 
'django.contrib.admin', 
'googlehooks', 
'registration'] 
Installed Middleware: 
('django.middleware.common.CommonMiddleware', 
'django.contrib.sessions.middleware.SessionMiddleware', 
'django.contrib.auth.middleware.AuthenticationMiddleware') 


Traceback: 
File "/Library/Python/2.6/site-packages/django/core/handlers/base.py" in get_response 
    92.     response = callback(request, *callback_args, **callback_kwargs) 
File "/Library/Python/2.6/site-packages/django/contrib/auth/decorators.py" in __call__ 
    78.    return self.view_func(request, *args, **kwargs) 
File "/Users/marcus/Sites/marcuswhybrow.net/autolib/libraries/forms.py" in  create_collection 
    13.   form = CreateCollectionForm(request.POST,  collection_type=collection_type, user=request.user) 

Exception Type: TypeError at /forms/create_bookshelf/hello 
Exception Value: __init__() got multiple values for keyword argument 'collection_type' 
+0

... так что вы уверены, что это не раздел #code, который вы опустили? В частности, что вы делаете с collection_type? – EMiller

+0

Я добавил полный код конструкторов. –

+0

Какая ошибка? Не могли бы вы опубликовать весь стек? – gruszczy

ответ

42

Вы передаете аргумент collection_type в качестве аргумента ключевого слова, потому что вы конкретно говорите collection_type=collection_type в своем обращении к конструктору формы. Таким образом, Python включает его в словарь kwargs, но поскольку вы также объявили его позиционным аргументом в определении этой функции, он пытается передать его дважды, отсюда и ошибка.

Однако то, что вы пытаетесь сделать, никогда не будет работать. У вас не может быть user=None, parent=Noneдо словарь *args, поскольку они уже являются kwargs, и args всегда должны появляться перед kwargs. Способ исправить это отбросить явное определение collection_type, пользователей и родителей, и извлечь их из kwargs в функции:

def __init__(self, *args, **kwargs): 
    collection_type = kwargs.pop('collection_type', None) 
    user = kwargs.pop('user', None) 
    parent = kwargs.pop('parent', None) 
+0

wow, который отлично работал. Огромное спасибо! –

+3

Python никогда не будет включать аргумент в kwargs, если он объявлен как формальный параметр. Кроме того, абсолютно верно наличие параметров со значениями по умолчанию перед кортежем args. Что-то еще здесь происходит. –

+0

Да, я согласен с вами, хотя этот более простой подход - лучший подход в целом, поэтому я доволен. Сложная реализация всегда связана с поиском ошибок в миксе. –

9

Это довольно просто: вы проходите request.POST и только после этого вы положили аргумент для collection_type. На какой запрос.POST будет поставлен? Для этого нет места. Смотреть это:

In [8]: class A: 
    ...:  def __init__(self, a, *args): 
    ...:   print a, args 
    ...:   
    ...:   

In [9]: A(None, a=None) 
--------------------------------------------------------------------------- 
TypeError         Traceback (most recent call last) 

/home/gruszczy/Programy/logbuilder/<ipython console> in <module>() 

TypeError: __init__() got multiple values for keyword argument 'a' 

Move request.POST куда-то в разговоре, но помните, что названные аргументы приходят после того, как те, которые таковыми не являются.

+0

, конечно, все остальные аргументы * называются *, поэтому request.POST должен быть передан в качестве первого аргумента? –

+0

@Marcus Whybrow: Да, request.POST должен быть передан как первый аргумент, но первый аргумент, объявленный для __init__, - это collection_type (self не учитывается) –

7
решением

Daniel Роземана является обрабатывать смесь *args и **kwargs лучше, но gruszczy лет объяснение является правильным:

вы определили CreateCollectionForm.__init__ с этой подписью:

def __init__(self, collection_type, user=None, parent=None, *args, **kwargs) 

И вы тогда называть это так:

form = CreateCollectionForm(
    request.POST, 
    collection_type=collection_type, 
    parent=parent, 
    user=request.user 
) 

self назначается неявным образом во время вызова. После этого Python видит только один позиционный аргумент: request.POST, который назначается как collection_type, первым параметром. Затем обрабатываются аргументы ключевого слова, и когда Python видит еще одно имя аргумента ключевого слова collection_type, тогда он должен вызывать TypeError.

Решение Daniel является хорошим, удалив все именованные параметры, гораздо проще справиться с такими вещами и передать их через super() конструкторам более высокого уровня. В качестве альтернативы вам нужно сделать первый словарь в качестве первого формального параметра для вашего метода __init__ и передать его в суперкласс.

+0

Я вижу, честно говоря, я не хотел прикасаться к request.POST из-за моих знаний о всех факторах, которые не являются первоклассными. Спасибо за разъяснение ответа gruszczy, хотя я чувствую, что я должен оставить Daniel Roseman в качестве основного ответа, поскольку он обеспечивает прямое решение моей конкретной проблемы, будет ли это правильным протоколом? –

+0

думаю есть. Если вы используете super() в конструкторе, это означает, что вы понимаете, что ваш конструктор может быть вызван как часть цепочки вызовов, и обычно лучше всего использовать * args и ** kwargs. –

Смежные вопросы