user1852176 user1852176 - 1 year ago 102
Python Question

Django form input missing in POST data

I'm using Django 1.9.8 and I'm trying to learn how to use form validation. I'm working on a form for registering users. I have 2 problems that I'm stuck on.

  1. The field for re-entering the password isn't accessible (rather, just not there at all) in the file methods. When I use
    raise Exception(self.cleaned_data.get('password'))
    to view the content, the password is shown. I can also view the username and email. When I do the same for the repassword field, it shows
    Edit - this was solved by changing the file to use a clean() method

  2. The validation errors are not displaying at all on my form. If there are errors the redirect back to the form is working, but there is nothing in the inputs for the user to fix, nor is the error displayed. Edit - solved by changing the RegisterForm call after the else statement to
    form = RegisterForm(request.POST)

Here is the file
class RegisterForm(forms.Form):
username = forms.CharField(label="Username", max_length=30,
widget=forms.TextInput(attrs={'class': 'form-control', 'name': 'username'}))
email = forms.CharField(label="Email", max_length=30,
widget=forms.TextInput(attrs={'class': 'form-control', 'name': 'email'}))
password = forms.CharField(label="Password", max_length=30,
widget=forms.TextInput(attrs={'class': 'form-control', 'name': 'password', 'type' : 'password'}))
repassword = forms.CharField(label="RePassword", max_length=30,
widget=forms.TextInput(attrs={'class': 'form-control', 'name': 'repassword', 'type' : 'password'}))

#Edit - this was changed to the clean() function in the selected answer.
def clean_password(self):
password1 = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('repassword')
#raise Exception(self.cleaned_data.get('repassword')) # None is displayed

if password1 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return self.cleaned_data

Here's the form template


{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
{% endfor %}
{% endfor %}
{% endif %}

<form method="post" action="" id="RegisterForm">
{% csrf_token %}
<p class="bs-component">
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
<td>{{ }}</td>
<td>{{ }}</td>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
<td>{{ form.repassword.label_tag }}</td>
<td>{{ form.repassword }}</td>
<p class="bs-component">
<input class="btn btn-success btn-sm" type="submit" value="Register" />
<input type="hidden" name="next" value="{{ next }}" />

Here's the view
class RegisterViewSet(viewsets.ViewSet):

#GET requests
def register(self,request):
return render(request, 'authorization/register.html', {'form': RegisterForm})

#POST requests
def create(self,request):
form = RegisterForm(request.POST)
if form.is_valid():
username = request.POST['username']
email = request.POST['email']
password = request.POST['password']
user = User.objects.create_user(username,email,password)
return HttpResponseRedirect('/users')
return render(request, 'authorization/register.html', {'form': RegisterForm })
#changed the previous line to the following. This fixed the errors not displaying
#return render(request, 'authorization/register.html', {'form': RegisterForm(request.POST) })

Answer Source

I am not %100 sure why self.cleaned_data.get('repassword') returns None in that method, however clean_password is not the right place for performing validations of fields that depend on each other.

According to docs, you should perform that kind of validation in clean() function:

def register(self,request):
    form = RegisterForm() # Notice `()` at the end
    return render(request, 'authorization/register.html', {'form': form})


def clean(self):
    cleaned_data = super(RegisterForm, self).clean()

    password1 = cleaned_data.get('password')
    password2 = cleaned_data.get('repassword')

    if password1 and password1 != password2:
        raise forms.ValidationError("Passwords don't match")

Please note that you don't have to implement clean_password and clean_repassword.

(If you still have to implement clean_password, you have to return password1 from it, not self.cleaned_data.)

You also need to render form errors correctly as described in the docs.

Don't forget to add it in your template:

{{ form.non_field_errors }}

As for the second error, the problem is, every time the validation fails you are returning a new fresh RegisterForm instance instead of the invalidated one.

You should change the line in create() function:

return render(request, 'authorization/register.html', {'form': RegisterForm}) 

to this:

return render(request, 'authorization/register.html', {'form': form})
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download