开发者

Django error: got multiple values for keyword argument

I get the following error when instantiating a Django form with a the constructor overriden:

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

The __init__() function (shown below) is exactly as written this but with # code replaced with my logic. Asside from that I am essentially overriding the form's (which is a ModelForm) constructor.

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

The call that creates the error is shown here:

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

I cannot see any reason why the error is popping up.

EDIT: Here is the full code for the constructor

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.AuthenticationMiddlewar开发者_高级运维e')


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'


You're passing the collection_type argument in as a keyword argument, because you specifically say collection_type=collection_type in your call to the form constructor. So Python includes it within the kwargs dictionary - but because you have also declared it as a positional argument in that function's definition, it attempts to pass it twice, hence the error.

However, what you're trying to do will never work. You can't have user=None, parent=None before the *args dictionary, as those are already kwargs, and args must always come before kwargs. The way to fix it is to drop the explicit definition of collection_type, user and parent, and extract them from kwargs within the function:

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


It's pretty simple: you pass request.POST and only afterward you put argument for collection_type. Onto what request.POST will be put? There is not place for that. Watch this:

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 somewhere else in the call, but remember, that named arguments come after the ones, that aren't.


Daniel Roseman's solution is to handle a mixture of *args and **kwargs better, but gruszczy's explanation is the correct one:

You have defined CreateCollectionForm.__init__ with this signature:

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

And you are then calling it like this:

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

self is assigned implicitly during the call. After that, Python sees only one positional argument: request.POST, which is assigned as collection_type, the first parameter. Then, the keyword arguments are processed, and when Python sees another keyword argument names collection_type, then it has to throw a TypeError.

Daniel's solution is a good one, by removing all named parameters, it is much easier to handle things like this, and to pass them up through super() to higher-level constructors. Alternately, you need to make the post dictionary the first formal parameter to your __init__ method, and pass it up to the superclass.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜