2010-08-20 15 views
13

Ricevo un KeyError per "password" quando provo a inviare il mio modulo.Metodo Django cleaning KeyError su POST

traccia:

Request Method: POST 
Request URL: http://localhost:8000/register/ 
Django Version: 1.2.1 
Python Version: 2.7.0 
Installed Applications: 
['django.contrib.auth', 
'django.contrib.contenttypes', 
'django.contrib.sessions', 
'djangoproject1.authentication'] 
Installed Middleware: 
('django.middleware.common.CommonMiddleware', 
'django.contrib.sessions.middleware.SessionMiddleware', 
'django.contrib.auth.middleware.AuthenticationMiddleware') 


Traceback: 
File "C:\Python27\lib\site-packages\django\core\handlers\base.py" in get_response 
    100.      response = callback(request, *callback_args, **callback_kwargs) 
File "C:\Users\jec23\My Java Projects\djangoproject1\src\djangoproject1\authentication\views.py" in register 
    20.   if rf.is_valid() and pf.is_valid(): 
File "C:\Python27\lib\site-packages\django\forms\forms.py" in is_valid 
    121.   return self.is_bound and not bool(self.errors) 
File "C:\Python27\lib\site-packages\django\forms\forms.py" in _get_errors 
    112.    self.full_clean() 
File "C:\Python27\lib\site-packages\django\forms\forms.py" in full_clean 
    268.   self._clean_form() 
File "C:\Python27\lib\site-packages\django\forms\forms.py" in _clean_form 
    296.    self.cleaned_data = self.clean() 
File "C:\Users\jec23\My Java Projects\djangoproject1\src\djangoproject1\authentication\forms.py" in clean 
    16.   if self.cleaned_data['cpassword']!=self.cleaned_data['password']: 

Exception Type: KeyError at /register/ 
Exception Value: 'password' 

vista:

def register(request): 
    if request.method == 'POST': 
     rf = forms.RegisterForm(request.POST) 
     pf = forms.ProfileForm(request.POST) 
     if rf.is_valid() and pf.is_valid(): 
      newuser = User(username=rf.cleaned_data['username'],email=rf.cleaned_data['email']) 
      newuser.set_password(rf.cleaned_data['password']) 
      newuser.save() 
      profile = pf.save(commit=False) 
      profile.user = newuser 
      profile.save() 
      return HttpResponseRedirect("/register-success/") 
     else: 
      return render_to_response("authentication/index.html", {'form1': rf, 'form2':pf}) 
    else: 
     return main(request) 

forme:

class RegisterForm(forms.Form): 
    username = forms.CharField(min_length=6,max_length=15) 
    password = forms.CharField(min_length=6,max_length=15,widget = forms.PasswordInput()) 
    cpassword = forms.CharField(label='Confirm Password',widget = forms.PasswordInput()) 
    email = forms.EmailField(label='E-mail Address') 

    def clean(self): 
     if self.cleaned_data['cpassword']!=self.cleaned_data['password']: 
      raise forms.ValidationError("Passwords don't match") 
     return self.cleaned_data 

class ProfileForm(forms.ModelForm): 
    phonenumber = forms.CharField(label='Phone Number') 

    class Meta: 
     model = UserProfile 
     exclude = ('user') 
+0

puoi pubblicare il codice che utilizza questo modulo? –

+0

incluso codice vista – JPC

+0

Puoi pubblicare anche il codice modello? –

risposta

10

Aha! Il messaggio di errore di convalida visualizzato non è in realtà un messaggio di errore di convalida. Lasciatemi spiegare. Quando si esegue il rendering l'istanza modulo modello utilizzando as_p, rende ogni campo nel modo seguente:

<p><label ...>fieldname</label> <input ... name="fieldname" /> HELP TEXT IF ANY</p> 

La stringa Required. 30 characters or fewer. Letters, numbers and @/./+/-/_ characters che state vedendo sul lato destro del campo non è altro che il testo di aiuto. Questo testo della guida è preso dalla definizione del modello: puoi verificarlo visitando django/contrib/auth/models.py e ispezionando la definizione della classe User.

Quando si esegue l'override del campo username, si omette qualsiasi testo di guida. Naturalmente l'"errore" scompare.

Per verificare questa teoria, attenersi alla seguente procedura nel metodo main.

def main(request): 
    uf = forms.UserForm() 
    upf = forms.UserProfileForm() 
    print "User form is bound:%s errors:%s" % (uf.is_bound, uf.errors) 
    return render_to_response("authentication/index.html", {'form1': uf, 'form2':upf}) 

Aggiorna

if self.cleaned_data['cpassword']!=self.cleaned_data['password']: 

Questa linea può causare problemi quando l'utente non fornisce una o entrambe password e cpassword. Ad esempio, provare questo dalla shell Django:

>>> data = dict(username = 'admin', cpassword = 'foo', email='[email protected]') 
>>> f = RegisterForm(data) 
>>> f.is_bound 
True 
>>> f.is_valid() 

Traceback (most recent call last): 
    ... 
    File "<pyshell#2>", line 8, in clean 
    if self.cleaned_data['cpassword']!=self.cleaned_data['password']: 
KeyError: 'password' 

cambiare il metodo del form clean per assicurarsi che entrambi i valori siano presenti prima di confrontare. Qualcosa di simile a questo:

def clean(self): 
    password = self.cleaned_data.get('password', None) 
    cpassword = self.cleaned_data.get('cpassword', None) 
    if password and cpassword and (password == cpassword): 
     return self.cleaned_data 
    else: 
     raise forms.ValidationError("Passwords don't match") 
+0

Ah!Questo ha un sacco di senso. Non l'avrei mai capito. Mi sono un po 'annoiato con il ModelForm per l'utente perché dovevo ridefinire i campi e stavo ancora ottenendo quell'Errore di Chiavi per qualche ragione. Ho finito per utilizzare un modulo regolare e solo la creazione dell'utente. – JPC

+0

È possibile far scomparire il testo della guida senza prevaricare? – JPC

+0

@JPC: è possibile sovrascrivere il metodo '__init__' del modulo e impostare' self.fields ['username']. Help_text = None'. Un po 'cretino IMHO. In alternativa è possibile utilizzare un altro metodo per visualizzare il modulo in cui si ignora il testo della guida. –

11

Dalla mia esperienza, ho scoperto che se si vuole fare un po 'di validazione in più campi, anche se sono contrassegnati come richiesto = True, quando l'override del metodo clean() nel modulo, se i campi che vuoi convalidare non sono pieni quando li invii e provi ad accedervi come cleaned_data["field_name"] il tuo codice esploderà con un numero KeyError. Per evitare ciò, è sufficiente accedere al campo in clean_data tramite il metodo del dizionario get() e verificare se è None o passare un valore predefinito. Come corollario:

my_field = cleaned_data.get("field_name") # This is safe and it will work! :) 
my_filed = cleaned_data["field_name"] # This will crash when the field was not filled! :(

Spero che questo aiuta a qualcun altro, ho perso una grande quantità di tempo a causa di questa cosa stupida!

+0

Questa dovrebbe essere la risposta – Gnoliz