Reputation: 299
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.
Upvotes: 0
Views: 2319
Reputation: 309049
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.
Upvotes: 2
Reputation: 13
you can try this as is described in the documentation:
def clean(self):
super(AuthorLogin, self).clean()
...
# not return anything
Upvotes: 0
Reputation: 3755
The clean
method needs to use super()
:
def clean(self):
cleaned_data = super(AuthorLogin, self).clean() #insert this line
username = cleaned_data.get('username')
...
Upvotes: 0