Question

This is my view:

def main_page(request):

    if request.method == 'POST':
        form = RegistrationForm(request.POST)
        if form.is_valid():
            user = User.objects.create_user(
                username=form.clean_data['username'],
                password=form.clean_data['password1'],
                email=form.clean_data['email']
            )
        return HttpResponseRedirect('/')
    else:
        form = RegistrationForm()
        variables = {
            'form': form
        }
        return render(request, 'main_page.html', variables)

and this is my main_page.html:

{% if form.errors %}
    <p>NOT VALID</p>
    {% for errors in form.errors %}
        {{ errors }}
    {% endfor %}
{% endif %}
<form method="post" action="/">{% csrf_token %}
    <p><label for="id_username">Username:</label>{{ form.username }}</p>
    <p><label for="id_email">Email Address:</label>{{ form.email }}</p>
    <p><label for="id_password">Password:</label>{{ form.password1 }}</p>
    <p><label for="id_retypePassword">Retype Password:</label>{{ form.password2 }}</p>
    <input type="hidden" name="next" />
    <input type="submit" value="Register" />
</form>

When I go to the url which uses the main_page view, it just displays the form. When I submit the form with errors (with blank fields and without a proper email address) it just redirects me to the same page and doesn't display any errors. It doesn't even say "NOT VALID".

When I change

{% if form.errors %}

to

{% if not form.is_valid %}

it always says "NOT VALID" (even if it is the first time going to the url and even if I didn't submit anything yet).

This is my RegistrationForm:

from django import forms
import re
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist

class RegistrationForm(forms.Form):
    username = forms.CharField(label='Username', max_length=30)
    email = forms.EmailField(label='Email')
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput())
    password2 = forms.CharField(label='Password (Again)', widget=forms.PasswordInput())

    def clean_password2(self):
        if 'password1' in self.cleaned_data:
            password1 = self.cleaned_data['password1']
            password2 = self.cleaned_data['password2']
            if password1 == password2:
                return password2
        raise forms.ValidationError('Passwords do not match.')

        def clean_username(self):
        username = self.cleaned_data['username']
        if not re.search(r'^\w+$', username): #checks if all the characters in username are in the regex. If they aren't, it returns None
            raise forms.ValidationError('Username can only contain alphanumeric characters and the underscore.')
        try:
            User.objects.get(username=username) #this raises an ObjectDoesNotExist exception if it doesn't find a user with that username
        except ObjectDoesNotExist:
            return username #if username doesn't exist, this is good. We can create the username
        raise forms.ValidationError('Username is already taken.')
Was it helpful?

Solution

It is redirecting you because you always return HttpResponseRedirect if the method is POST, even if the form is not vaild. Try this:

def main_page(request):
    form = RegistrationForm()
    if request.method == 'POST':
        form = RegistrationForm(request.POST)
        if form.is_valid():
            user = User.objects.create_user(
                username=form.clean_data['username'],
                password=form.clean_data['password1'],
                email=form.clean_data['email']
            )
            return HttpResponseRedirect('/')        
    variables = {
        'form': form
    }
    return render(request, 'main_page.html', variables)

That way, the form instance, on which is_valid was called, is passed to the template, and it has a chance to display the errors. Only if the form is valid, the user is redirected. If you want to be fancy, add a message using the messages framework before redirecting.

If you want it a little bit more concise:

def main_page(request):
    form = RegistrationForm(request.POST or None)
    if form.is_valid():
        user = User.objects.create_user(
            username=form.clean_data['username'],
            password=form.clean_data['password1'],
            email=form.clean_data['email']
        )
        return HttpResponseRedirect('/')        
    variables = {
        'form': form
    }
    return render(request, 'main_page.html', variables)

OTHER TIPS

Make your view something like this:

if form.is_valid():
    pass
    # actions
else:
    # form instance will have errors so we pass it into template
    return render(request, 'template.html', {'form': form})

And in the templates you can iterate over form.errors or simple:

{{ forms.as_p }}

You can reformat your view to display the form errors in the console as below

def main_page(request):

    if request.method == 'POST':
        form = RegistrationForm(request.POST)
        if form.is_valid():
            user = User.objects.create_user(
                username=form.clean_data['username'],
                password=form.clean_data['password1'],
                email=form.clean_data['email']
            )
        else:
            print(form.errors)
            return HttpResponse("Form Validation Error")

        return HttpResponseRedirect('/')
    else:
        form = RegistrationForm()
        variables = {
            'form': form
        }
        return render(request, 'main_page.html', variables)


As always, keep the indent as above. If the form has any error , it will display in the console like

<ul class="errorlist"><li>date<ul class="errorlist"><li>Enter a valid date.</li></ul></li></ul>

Hope it helps

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top