0

I just want to have an error message when user failed to login due to invalid username or password. Is there any preferable why to have it than overriding clean method. I found that django have login_failed signals but i am unsure it best to use that.

Here is the print screen validation error message get displayed twice

Here is my updated code from github :

https://github.com/afdallismen/Django-error-message-displayed-twice

Here is my python version and output from pip list, and pip freeze

python -V

Python 3.5.2

pip list

Django (1.10.1)
pip (8.1.2)
setuptools (27.1.2)
wheel (0.29.0)

pip freeze

Django==1.10.1

forms.py

class AuthorLogin(forms.Form):
    username = forms.CharField(label='Your name', max_length=100)
    password = forms.CharField(
                           label='Your password',
                           max_length=100,
                           widget=forms.PasswordInput)

    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('Invalid username or password', code='invalid')
        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

    def form_invalid(self, form):
        return self.render_to_response(self.get_context_data(form=form))

view.py

def author_login(request):
    form = AuthorLogin(request.POST or None)
    if request.POST and form.is_valid():
        user = form.login(request)
        if user is not None:
            login(request, user)
            return redirect('microblog:index')

    return render(request, 'microblog/author_login.html', {'form': form})

urls.py

app_name = 'microblog'
urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^login/', views.author_login, name='author_login'),
    url(r'^logout/', views.author_logout, name='author_logout'),
]

author_login.html

{% extends "base.html" %}

{% block content %}
{% if form.non_field_errors %}
  <ul>
    {% for error in form.non_field_errors %}
     <li>{{ error }}</li>
    {% endfor %}
  </ul>
{% endif %}
<form action="" method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <input type="submit" value="Login" />
</form>
{% endblock %}

base.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Microblog</title>
</head>
<body>
  {% if user.is_authenticated %}
    <p>Welcome, {{ request.user.username }}</p>
    <a href="{% url 'microblog:author_logout' %}">Logout</a>
  {% else %}
    <a href="{% url 'microblog:author_login' %}">Login</a>
  {% endif %}
  <nav>
    <ul>
      <li><a href="{% url 'microblog:index' %}">Microblog</a></li>
    </ul>
  </nav>
  {% block content %}{% endblock %}
</body>
</html>

As a workaround i tried to sent validation error through form.add_error method in views.py, and removing clean, login and form_invalid method on forms.py.

Here is my new views.py :

def author_login(request):
    if request.method == 'POST':
        form = AuthorLogin(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = authenticate(username=username, password=password)
            if user is not None:
                login(request, user)
                return redirect('microblog:index')
            else:
                form.add_error(None, 'Invalid username or password.') # This line seems to get executed twice
    else:
        form = AuthorLogin()

    return render(request, 'microblog/author_login.html', {'form': form})

Turn out it still displayed error message twice. Now it seems form.add_error was called twice, was it ?.

Tried this :

def author_login(request):
    if request.method = 'POST':
        count = 0
        ...
        if form.is_valid()
            ....
            if user is not None:
                ....
            else:
                count = count + 1
                print(count)

It print 1 and only printed once, so author_login with POST method just executed once, same for else block that print count. But form.add_error get executed twice ?

Fix it

It seems form.non_field_errors was included in form.errors . Deleting call to forms.non_field_errors in templates show the error message only once.

3
  • You should override this method in your AuthorLogin form. Commented Sep 22, 2016 at 17:14
  • tried to copy and paste solution from link above in my AuthorLogin from still give me two error message. Commented Sep 22, 2016 at 17:20
  • Note that Django comes with authentication views and forms. You don't have to write your own. Commented Nov 11, 2016 at 11:46

3 Answers 3

2

The error is displayed once when you loop through form.non_field_errors

<ul>
  {% for error in form.non_field_errors %}
    <li>{{ error }}</li>
  {% endfor %}
</ul>

and once when you use form.as_p

{{ form.as_p }}

If you use form.as_p, then you don't have to render form.non_field_errors manually. If you do display form.non_field_errors manually, then you'll have to render the form fields individually instead of using form.as_p.

See the docs on working with forms in templates for more info.

Sign up to request clarification or add additional context in comments.

Comments

0

The clean method needs to use super():

def clean(self):
    cleaned_data = super(AuthorLogin, self).clean() #insert this line
    username = cleaned_data.get('username')
    ...

3 Comments

Try adding this custom form_invalid method to your form: github.com/ubernostrum/django-registration/commit/…
Can you edit in the form_invalid you're using so we can double check it? Assuming it's right, this is probably an error in the registration package you're using. Since it's calling clean twice, the easy fix would be to remove your custom clean logic and check whether the login was successful or not in your view.
Edited my question, now i tried sending error message through from.add_error method in a my view.py, and removing clean, login, and form_invalid method in froms.py. But it still displayed two error message.
0

you can try this as is described in the documentation:

def clean(self):
    super(AuthorLogin, self).clean()
    ...
    # not return anything

3 Comments

Tried that, still displaying two error message. I have edited my question, right now i am using form.add_error in views.py and removing clean method in my form.py. But error message still displayed twice.
Check your base.html if it has printed the error already. That is more convincing than the only one sentence get executed twice.
Still displayed 2 error message even i am not extend 'base.html'.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.