2009-12-21 14 views
20

me sale el siguiente error al crear instancias de un formulario de Django con el constructor anulado:error de Django: conseguido varios valores para argumento de palabra clave

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

La función __init__() (mostrada a continuación) es exactamente como está escrito esto, pero con # code reemplazado con mi lógica. A partir de eso, estoy básicamente anulando el constructor del formulario (que es un ModelForm).

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

La llamada que crea el error se muestra aquí:

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

no puedo ver ninguna razón por la que el error está surgiendo.

EDIT: Aquí está el código completo para el 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.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

... ¿así que estás seguro de que no es la sección #code que has omitido? Específicamente, ¿qué estás haciendo con collection_type? – EMiller

+0

He agregado el código completo de los constructores. –

+0

¿Cuál es el tipo del error? ¿Podrías publicar toda la stacktrace? – gruszczy

Respuesta

42

Está pasando el argumento collection_type en un argumento de palabra clave, porque especifica collection_type=collection_type en su llamada al constructor de formulario. Así que Python lo incluye dentro del diccionario kwargs, pero como también lo ha declarado como un argumento posicional en la definición de esa función, intenta pasarlo dos veces, de ahí el error.

Sin embargo, lo que intenta hacer nunca funcionará. No puede tener user=None, parent=Noneantes de el diccionario *args, ya que esos ya son kwargs, y los argumentos siempre deben venir antes que los kwargs. La forma de solucionarlo es dejar caer la definición explícita de collection_type, el usuario y el padre, y extraerlos de kwargs dentro de la función:

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

wow, que funcionó perfectamente. ¡Muchas gracias! –

+3

Python nunca incluirá un argumento en kwargs si se declara como un parámetro formal. Además, es absolutamente válido tener parámetros con valores predeterminados antes de la tupla args. Algo más está pasando aquí. –

+0

Sí, estoy de acuerdo con usted, aunque este enfoque más simple es un enfoque mejor en general, por lo que estoy contento. Una implementación complicada siempre está obligada a arrojar difícil detectar errores en la mezcla. –

9

Es bastante simple: se pasa request.POST y sólo después se puso argumento para collection_type. ¿En qué solicitud. Se colocará LA POST? No hay lugar para eso. Mira este:

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' 

Mover request.POST en otra parte de la llamada, pero recuerda que los argumentos con nombre vendrán después, los que no lo son.

+0

seguramente todos los demás argumentos * son * nombrados, por lo tanto request.POST debe pasar como el primer argumento? –

+0

@Marcus Whybrow: Sí, request.POST se debe pasar como primer argumento, pero el primer argumento declarado para __init__ es collection_type (self does not count) –

7
solución de

Daniel Roseman es manejar una mezcla de *args y **kwargs mejor, pero gruszczy de explicación es la correcta:

ha definido CreateCollectionForm.__init__ con esta firma:

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

Y, a continuación, se llama así:

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

self se asigna implícitamente durante la llamada. Después de eso, Python solo ve un argumento posicional: request.POST, que se asigna como collection_type, el primer parámetro. A continuación, se procesan los argumentos de la palabra clave, y cuando Python ve otro nombre de argumento de palabra clave collection_type, tiene que arrojar un TypeError.

La solución de Daniel es buena, eliminando todos los parámetros nombrados, es mucho más fácil manejar este tipo de cosas y pasarlas a través de super() a constructores de alto nivel. Alternativamente, debe convertir el diccionario de publicaciones en el primer parámetro formal de su método __init__ y pasarlo a la superclase.

+0

Veo, para ser honesto, no quería tocar la solicitud.POST debido a mi conocimiento de todos los factores contribuyentes que no son de primera categoría. Gracias por aclarar la respuesta de gruszczy, aunque creo que debería dejar la respuesta principal de Daniel Roseman, ya que proporciona una solución directa a mi problema específico, ¿sería ese el protocolo correcto? –

+0

Creo que sería. Si está utilizando super() en un constructor, eso significa que reconoce que su constructor podría ser llamado como parte de una cadena de llamadas, y generalmente es mejor usar solo * args y ** kwargs. –

Cuestiones relacionadas