2009-12-21 5 views
20

ich folgende Fehlermeldung erhalten, wenn eine Django-Form mit einem Konstruktor instanziieren außer Kraft gesetzt:Django Fehler: got mehrere Werte für die Keyword-Argument

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

Die __init__() Funktion (siehe unten) ist genau so, wie dies geschrieben, aber mit # code ersetzt durch meine Logik. Davon abgesehen übertrage ich im Wesentlichen den Konstruktor des Formulars (welcher ein ModelForm ist).

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

Der Aufruf, der den Fehler erzeugt wird hier gezeigt:

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

ich keinen Grund sehen, warum der Fehler oben knallt.

EDIT: Hier ist der vollständige Code für den Konstruktor

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

... also sind Sie sicher, dass nicht der #code-Abschnitt weggelassen wurde? Was genau machst du mit collection_type? – EMiller

+0

Ich habe den vollständigen Code des Konstruktors hinzugefügt. –

+0

Was ist der Fehlertyp? Könntest du den ganzen Stacktrace posten? – gruszczy

Antwort

42

Sie übergeben das Argument collection_type als Schlüsselwortargument, weil Sie in Ihrem Aufruf des Formularkonstruktors ausdrücklich collection_type=collection_type sagen. Also fügt Python es in das Wörterbuch ein - aber da Sie es auch als positionales Argument in der Definition dieser Funktion deklariert haben, versucht es, es zweimal zu übergeben, daher der Fehler.

Allerdings, was Sie versuchen, wird nie funktionieren. Sie können nicht user=None, parent=Nonevor das *args Wörterbuch haben, da diese bereits Kwargs sind, und Args müssen immer vor Kwargs kommen. Die Art und Weise, es zu beheben ist die explizite Definition von collection_type, Benutzern und Eltern fallen und sie von kwargs innerhalb der Funktion extrahieren:

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

wow, das hat perfekt funktioniert. Vielen Dank! –

+3

Python wird niemals ein Argument in Kwargs enthalten, wenn es als formaler Parameter deklariert ist. Außerdem ist es absolut gültig, Parameter mit Standardwerten vor dem args-Tupel zu haben. Hier läuft noch etwas anderes. –

+0

Ja, ich stimme Ihnen zu, obwohl dieser einfachere Ansatz im Allgemeinen ein besserer Ansatz ist, also bin ich zufrieden. Eine komplizierte Implementierung ist immer verpflichtet, Fehler in den Mix zu finden. –

9

Es ist ziemlich einfach: Sie request.POST passieren und erst danach setzen Sie Argument für collection_type. Auf welche Anfrage.POST wird gestellt? Dafür gibt es keinen Platz. 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' 

verschieben request.POST woanders in dem Ruf, aber denken Sie daran, dass die benannten Argumente kommen, nachdem diejenigen, die nicht sind.

+0

sind sicherlich alle anderen Argumente * benannt, also muss request.POST als erstes Argument übergeben werden? –

+0

@Marcus Whybrow: Ja, request.POST muss als erstes Argument übergeben werden, aber das erste für __init__ deklarierte Argument ist collection_type (self zählt nicht) –

7

Daniel Roseman-Lösung ist eine Mischung aus *args und **kwargs besser zu handhaben, aber gruszczy der Erklärung ist die richtige:

Sie definiert haben CreateCollectionForm.__init__ mit dieser Signatur:

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

Und Sie werden dann es so nennen:

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

self wird implizit während des Aufrufs zugewiesen. Danach sieht Python nur ein Positionsargument: request.POST, das als collection_type, der erste Parameter, zugewiesen ist. Dann werden die Schlüsselwortargumente verarbeitet, und wenn Python ein anderes Schlüsselwortargument Names collection_type sieht, muss es ein TypeError auslösen.

Daniels Lösung ist eine gute, indem man alle genannten Parameter entfernt, es ist viel einfacher, solche Dinge zu handhaben und sie über super() an höhere Konstrukteure weiterzugeben. Alternativ müssen Sie das Post-Dictionary zum ersten formalen Parameter für Ihre __init__-Methode machen und es an die Superklasse übergeben.

+0

Ich sehe, um ehrlich zu sein, ich wollte nicht die Anfrage zu berühren.POST aufgrund meines Wissens über alle beitragenden Faktoren nicht erstklassig sein. Danke für die Klärung der Antwort von Gruszczy, obwohl ich denke, ich sollte Daniel Roseman als primäre Antwort verlassen, da es eine direkte Lösung für mein spezifisches Problem bietet, wäre das das richtige Protokoll? –

+0

Ich denke, dass es sein würde. Wenn Sie super() in einem Konstruktor verwenden, bedeutet dies, dass Sie erkennen, dass Ihr Konstruktor als Teil einer Kette von Aufrufen aufgerufen werden kann, und es ist im Allgemeinen am besten, nur * args und ** kwargs zu verwenden. –

Verwandte Themen