Django. Error message for login form

Wolter picture Wolter · Feb 26, 2013 · Viewed 33.7k times · Source

I make login/password form:

model:

class LoginForm(forms.Form):
username = forms.CharField(max_length=100)
password = forms.CharField(widget=forms.PasswordInput(render_value=False),max_length=100)

view:

def login_view(request):
    if request.method == 'POST':
        username = request.POST['email']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user is not None and user.is_active:
            login(request, user)
            return HttpResponseRedirect("/n1.html")# Redirect to a success page.
        return HttpResponseRedirect("/login")
    form=LoginForm()
    return render(request, 'enter.html', {'login_form': LoginForm})

urls:

(r'^login/$', login_view),

template:

{% if form.errors %}
<p>Something is wrong</p>
{% endif %}

<form class="form-signin" action="" method="post">
    <h2 class="form-signin-heading">Login</h2>
    {% csrf_token %}

    <input class="input-block-level" type="text" name="email" value="" id="email" placeholder="Email">

    <input class="input-block-level" type="password" name="password" value="" id="username" placeholder="Password">

    <button class="btn btn-large btn-primary" type="submit">Login</button>
    <input type="hidden" name="next" value="{{next|escape}}" />

</form>

I use redirect to login page then login or password is wrong, but I want to make error message in this case. Why construction {% if form.errors %} doesn't work? Thx!

Answer

Jack Shedd picture Jack Shedd · Feb 26, 2013

Because the form has no idea an error occurred.

When you construct the form here:

form=LoginForm()

You're constructing it without passing it any information. It doesn't know anything about the POST the user just did, or that the the login failed, or that the password was missing, or whatever the error was.

Here's what my login forms look like:

class LoginForm(forms.Form):
    username = forms.CharField(max_length=255, required=True)
    password = forms.CharField(widget=forms.PasswordInput, required=True)

    def clean(self):
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')
        user = authenticate(username=username, password=password)
        if not user or not user.is_active:
            raise forms.ValidationError("Sorry, that login was invalid. Please try again.")
        return self.cleaned_data

    def login(self, request):
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')
        user = authenticate(username=username, password=password)
        return user

We override the form's clean method, so that if the form passes validation we can check the user's credentials. We also put a login method on the form object itself, to make our view cleaner.

Next, in our view, we want to do this:

def login_view(request):
    form = LoginForm(request.POST or None)
    if request.POST and form.is_valid():
        user = form.login(request)
        if user:
            login(request, user)
            return HttpResponseRedirect("/n1.html")# Redirect to a success page.
    return render(request, 'enter.html', {'login_form': form })

We instantiate the form and hand it the request.POST to check against.

If we have a POST, we check if the form is valid. The form's "clean" method will get called, and check the user credentials for us.

If the credentials fail, we raise an error, which we need to show in our template.

Errors raised by the form (but not attached to a field) are stored in non_field_errors, which can be displayed like so:

{% if form.non_field_errors %}
    <ul class='form-errors'>
        {% for error in form.non_field_errors %}
            <li>{{ error }}</li>
        {% endfor %}
    </ul>
{% endif %}