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.
精彩评论