I am using Learning Website Development with Django book.
I have downloaded the source code from the fallowing path Source code in github. I am able to run the application in my windows machine.
However for some reason whenever login or registration from contains errors after submit, these errors are not shown in the html page.
Kindly let me know what is the issue. One obeseration what I saw in the browser console is
Following line of code displays error message
def register_page(request):
form = RegistrationForm(request.POST or None)
print request.method
if request.method == 'POST' and form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
email=form.cleaned_data['email']
)
return HttpResponseRedirect('/register/success/')
else:
form = RegistrationForm()
variables = RequestContext(request, {'form': form})
print variables
return render_to_response('registration/register.html', variables)
registration page html code:
{% extends "base.html" %}
{% block title %}User Registration{% endblock %}
{% block head %}User Registration{% endblock %}
{% block content %}
print "ddd"
{% if form.has_errors %}
<p>Your username and password didn't match.
Please try again.</p>
{% endif %}
<form method="post" action="">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="register" />
</form>
{% endblock %}
Login page code:
{% extends "base.html" %}
{% block title %}User Login{% endblock %}
{% block head %}User Login{% endblock %}
{% block content %}
{% if form.has_errors %}
<p>Your username and password didn't match.
Please try again.</p>
{% endif %}
<form method="post" action="">
{% csrf_token %}
<p><label for="id_username">Username:</label>
{{ form.username }}</p>
<p><label for="id_password">Password:</label>
{{ form.password }}</p>
<input type="submit" value="login" />
<input type="hidden" name="next" value="/" />
</form>
{% endblock %}
form.py
import re
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User
from django import forms
class RegistrationForm(forms.Form):
print 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()
)
# password validation:
def cleaned_password2(self):
# all valid values are accessible trough self.clean_data
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.')
# username validation
def cleaned_username(self):
username = self.cleaned_data['username']
if not re.search(r'^\w+$', username):
raise forms.ValidationError('Username can only contain alphanumeric characters and the underscore.')
try:
User.objects.get(username=username)
except ObjectDoesNotExist:
return username
raise forms.ValidationError('Username is already taken.')
class BookmarkSaveForm(forms.Form):
url = forms.URLField(
label='URL',
widget=forms.TextInput(attrs={'size': 64})
)
title = forms.CharField(
label='Title',
widget=forms.TextInput(attrs={'size': 64})
)
tags = forms.CharField(
label='Tags',
widget=forms.TextInput(attrs={'size': 64})
)
http://localhost:8000/static/style.css Failed to load resource: the server responded with a status of 404 (Not Found)
The code is simply wrong.
By combining the method == 'POST' and form.is_valid() into one check, they're ensuring that a new - empty - form is created if either of those things are false. What they should be doing is only creating an empty form if method does not equal "POST"; if the form is not valid, they need to return that invalid form to the template, as it contains the errors.
So you need to follow the normal pattern, with two separate if statements:
def register_page(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(
username=form.cleaned_data['username'],
password=form.cleaned_data['password1'],
email=form.cleaned_data['email']
)
return HttpResponseRedirect('/register/success/')
else:
form = RegistrationForm()
variables = RequestContext(request, {'form': form})
I Remenber i got the same problem, the thing i did is:
Use self.add_error to attach errors to a specific field in the form. Call add_error() instead of forms.ValidationError(), like this:
password validation:
def cleaned_password2(self):
# all valid values are accessible trough self.clean_data
if 'password1' in self.cleaned_data:
password1 = self.cleaned_data['password1']
password2 = self.cleaned_data['password2']
if password1 == password2:
return password2
msg = 'Passwords do not match.'
self.add_error('password1',msg)
Related
I'm creating an app in which I'd like to use my own custom login form with a captcha field. My intention is to do this without using an external library (except for requests) but I couldn't add captcha field to my custom form in forms.py, so I added it directly to login.html but for some reason when I do form.is_valid() it returns an error.
I've already seen the solutions in Django - adding google recaptcha v2 to login form and Adding a Recaptcha form to my Django login page but as I said, I'd like to do this without using an external library.
views.py
...
def login_view(request):
if request.method == 'POST':
form = CustomLoginForm(request.POST)
result = is_recaptcha_valid(request)
print(result) # prints True
if form.is_valid():
username = form.cleaned_data['username']
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Redirect to index
messages.success(request, "Logged in.")
return HttpResponseRedirect(reverse('orders:index'))
else:
messages.error(request, "Invalid credentials.")
else:
print("error")
return render(request, 'registration/login.html', {'form': CustomLoginForm()})
else:
form = CustomLoginForm()
return render(request, 'registration/login.html', {'form': form})
forms.py
class CustomLoginForm(AuthenticationForm):
email = forms.EmailField(
error_messages={
'required': 'Please enter your email.',
'invalid': 'Enter a valid email address.'
},
help_text='Email',
)
login.html
<form class="" action="{% url 'orders:login' %}" method="post">
{% csrf_token %}
{% for field in form %}
<p>
{{ field.label_tag }}<br>
{{ field }}
{% if field.help_text %}
<small style="color: grey">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</p>
{% endfor %}
<!-- ReCAPTCHAV3 -->
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey='key-here'></div>
<button class="btn btn-success" type="submit" name="">Login</button>
<!-- <input type="hidden" name="next" value="{{ next }}"> -->
</form>
is_recaptcha_valid() function already returns True so I didn't share that. I'm a beginner in Django, so if you can please explain in two words what I've done wrong instead of just posting the answer, I'd be grateful. Thank you for your time.
The AuthenticationForm is slightly different than the others..
If your check AuthenticationForm class, AuthenticationForm 's first arguments is not data like others form:
class AuthenticationForm(forms.Form):
...
def __init__(self, request=None, *args, **kwargs):
...
Thats why you need to pass request.POST to data.
So update your code like this:
def login_view(request):
if request.method == 'POST':
form = CustomLoginForm(data=request.POST)
...
This is my view:
if request.method == 'POST':
form = TeacherRegister(request.POST)
#Gets school object from email domain.
email = form['email'].value().split('#')[1]
try:
school = School.objects.get(email_domain = email)
except ObjectDoesNotExist:
#add custom error message here
if form.is_valid():
user, Teacher = form.save()
Teacher.school = school
Teacher.save()
user.groups.add(Group.objects.get(name='Teacher'))
#user.is_active to stop users logging in without confirming their emails
user.is_active = False
user.save()
#Sends confirmation link.
send_confirmation_email(request, user)
args = {'email': user.email,
'link': user.Teacher.school.email_website}
return render(request, 'email/token_sent.html', args)
else:
args = {'form': form,}
return render(request, 'users/teachers.html', args)
These lines are what I am trying to work with:
email = form['email'].value().split('#')[1]
try:
school = School.objects.get(email_domain = email)
except ObjectDoesNotExist:
#add custom error message here
This is the HTML I have for the email field:
<div class="required field" >
{{ form.email.label }}
{{ form.email }}
{{ form.email.help_text }}
<!-- <label>Email</label>
<input placeholder="Email" type="email" name="email" autofocus="" required="" id="id_email"> -->
</div>
How can I get it to say, if no school object is returned, something along the lines of 'School not found, check your email'?
Thanks
You need to perform validation on the form side.
Implement clean_email method in the form:
def clean_email(self):
email = self.cleaned_data.get('email')
email = email.split('#')[1]
try:
school = School.objects.get(email_domain = email)
except ObjectDoesNotExist:
raise forms.ValidationError(''School not found, check your email')
return email
Now in template you can show this error right after email field:
{{ form.email.label }}
{{ form.email }}
{{ form.email.errrors }}
{{ form.email.help_text }}
You can try Django's built-in messages framework. Try this:
try:
school = School.objects.get(email_domain = email)
except ObjectDoesNotExist:
messages.error(request, 'School not found, check your email')
And then somewhere above your form, add this:
{% if messages %}
{% for message in messages %}
<div class="alert {% if message.tags %} alert-{{ message.tags }}{% endif %}">{{ message|safe }}</div>
{% endfor %}
{% endif %}
Hope it helps!
I am building a register form, but I am having some trouble with its validation.
I would like to see the error message showing up at the field, but instead I am getting a error on the browser saying :
The User could not be created because the data didn't validate.
Request Method: POST
Request URL: http://127.0.0.1:8000/account/register/
Django Version: 1.9.8
Exception Type: ValueError
Exception Value:
The User could not be created because the data didn't validate.
Exception Location: C:\Python34\lib\site-packages\django\forms\models.py in save, line 446
This is my forms.py
class UserRegistrationForm(forms.ModelForm):
password = forms.CharField(label='Password', required=False ,widget=forms.PasswordInput)
password2 = forms.CharField(label='Repeat password', required=False ,widget=forms.PasswordInput)
class Meta:
model = User
fields = ('username', 'first_name', 'email')
def clean_password2(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
#cd = self.cleaned_data
if not password2:
raise forms.ValidationError("Fill out the password2 .")
if password1 != password2:
raise forms.ValidationError("The two password fields didn't match.")
return password2
This is my view register
def register(request):
if request.method == 'POST':
user_form = UserRegistrationForm(request.POST)
if user_form.is_valid:
new_user = user_form.save(commit=False)
new_user.set_password(user_form.cleaned_data['password'])
new_user.save()
return render(request, 'account/register_done.html', {'new_user': new_user})
else:
print (user_form.errors)
else:
user_form = UserRegistrationForm()
return render(request, 'account/register.html', {'user_form': user_form})
my htmls - register.html
{% extends "account/base.html" %}
{% block title %}Create an account{% endblock %}
{% block content %}
<h1>Create an account</h1>
<p>Please, sign up using the following form:</p>
<form action="." method="post">
{{ user_form.as_p }}
{% csrf_token %}
<p><input type="submit" value="Create my account"></p>
</form>
{% endblock %}
register_done.html
{% extends "account/base.html" %}
{% block title %}Welcome{% endblock %}
{% block content %}
<h1>Welcome {{ new_user.first_name }}!</h1>
<p>Your account has been successfully created. Now you can log in.</p>
{% endblock %}
Seems like you are not calling is_valid method, this may cause this issue:
if user_form.is_valid
Try to change above line to:
if user_form.is_valid()
I've built a "firewall" login form that I want to put in front of my
actual production website while I develop the site. The idea is to try
and keep the "bad guys" out of the site and at the same time see what
usernames and passwords they're using. The problem I'm having is that
if I enter an invalid username/password pair, my form's error message
doesn't get displayed. I realize that for my purposes, it it might be
better to not display any error message at all but I'd still like to
understand what the problem is. Can anyone see what I'm doing wrong?
Thanks.
# views.py
import logging
logger = logging.getLogger(__name__)
from django.contrib.auth import authenticate
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.views import login
from django.http import HttpResponseRedirect
def firewall_login(request, *args, **kwargs):
if request.method == "POST":
form = AuthenticationForm(request, data=request.POST)
username = request.POST['username']
password = request.POST['password']
if form.is_valid():
fw_username = form.cleaned_data['username']
fw_password = form.cleaned_data['password']
user = authenticate(username=fw_username, password=fw_password)
if user is not None:
if user.is_active:
login(request, user)
logger.info("User '%s' logged in." % fw_username)
return HttpResponseRedirect("/accounts/profile/")
else:
logger.info("User '%s' tried to log in to disabled account." % fw_username)
return HttpResponseRedirect("/accounts/disabled/")
else:
logger.info("User '%s' tried to log in with password '%s'." % (username, password))
form = AuthenticationForm(request) # Display bound form
else:
form = AuthenticationForm() # Display unbound form
return render(request, "registration/login.html", {"form": form,})
# login.html
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p class="alert alert-error">Sorry, that's not a valid username or password</p>
{% endif %}
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for field in form.non_field_errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<form action="" method="post">
{% csrf_token %}
<p><label for="username">Username:</label>{{ form.username }}</p>
<p><label for="password">Password:</label>{{ form.password }}</p>
<input type="hidden" name="next" value="{{ next|escape }}" />
<input class="btn btn-primary" type="submit" value="login" />
</form>
{% endblock %}
It's because you pass new form instance. Validation occurs on is_valid call.
So, just remove form = AuthenticationForm(request) in else block:
def firewall_login(request, *args, **kwargs):
if request.method == "POST":
form = AuthenticationForm(request, data=request.POST)
username = request.POST['username']
password = request.POST['password']
if form.is_valid():
fw_username = form.cleaned_data['username']
fw_password = form.cleaned_data['password']
user = authenticate(username=fw_username, password=fw_password)
if user is not None:
if user.is_active:
login(request, user)
logger.info("User '%s' logged in." % fw_username)
return HttpResponseRedirect("/accounts/profile/")
else:
logger.info("User '%s' tried to log in to disabled account." % fw_username)
return HttpResponseRedirect("/accounts/disabled/")
else:
logger.info("User '%s' tried to log in with password '%s'." % (username, password))
else:
form = AuthenticationForm() # Display unbound form
return render(request, "registration/login.html", {"form": form,})
I have a form for registration. When I try to register, it does and everything is fine, just that, its now showing any Form Validation Errors in the template, when there should be.
forms.py:
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django import forms
import re
from django.core.exceptions import ObjectDoesNotExist
class UserCreationForm(UserCreationForm):
class Meta:
model = User
fields = ('first_name', 'last_name', 'username',)
username = forms.EmailField(label='Email', max_length=250)
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.email = user.username
user.save()
return user
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('Password do not match.')
def clean_username(self):
username = self.cleaned_data['username']
if not re.search(r'^\w[\w.]*#[\w.]+$', username):
raise forms.ValidationError('Please enter a valid email address.')
try:
User.objects.get(username=username)
except ObjectDoesNotExist:
return username
raise forms.ValidationError('Email address is already taken.')
views.py:
def register_user(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register_success')
else:
print form.is_valid()
print form.errors
args = {}
args.update(csrf(request))
args['form'] = UserCreationForm()
print args
return render_to_response('register.html', args)
register.html:
{% extends "base.html" %}
{% block content %}
<h1>Registration:</h1>
<form method="post" action=".">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Register" />
</form>
{% endblock %}
I even tried {% if form.errors %}:
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p>{{form.errors }}</p>
{% endif %}
<h1>Registration:</h1>
<form method="post" action=".">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Register" />
</form>
{% endblock %}
But with no luck. I think I am missing something.
The reason is, if there are errors, you are overwriting it with form=UserCreationForm()
Basically, if if form.is_valid() is False, you need to send the form unaltered. By calling form=UserCreationForm after form.is_valid() you are overriding the errors
Try this:
def register_user(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/accounts/register_success')
else:
print form.is_valid()
print form.errors
else:
form = UserCreationForm()
return render_to_response('register.html', {'form': form}, context_instance=RequestContext(request))