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!
Related
I have a class based view which shows a login-form.
The problem is that I can't display error messages. I am trying to send an error message in a parameter in the URL to display it inside the HTML template file. But it does not work.
Here is my code so far:
forms.py
# a class which act as a view - it displays the login-form
class LoginForm(AuthenticationForm, BaseLoginView):
username=forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
password=forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control'}))
def get_context_data(self, **kwargs):
context = super(LoginForm, self).get_context_data(**kwargs)
context['error'] = ''
return context
urls.py
urlpatterns = [
path('login/', views_auth.LoginView.as_view(form_class=LoginForm, redirect_authenticated_user=True), name='login'), # login-page
]
views.py
# login functionality for the user
def custom_user_login(request):
if request.method == 'GET':
error_message = ''
return redirect('home')
elif request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
error_message = ''
# if the username & password is correct
user = authenticate(request, username=username, password=password)
if user is not None:
# Redirecting to the required login according to user type (admin / regular-user)
if user.is_superuser or user.is_staff:
login(request, user)
return redirect('admin_section/')
else:
login(request, user)
return redirect('/')
# display error message
else:
base_url = reverse('login') # /login/
query_string = urlencode({'error': 'The username & password combination are incorrect - please try again!'}) # error=The username & password combination are incorrect - please try again!
url = '{}?{}'.format(base_url, query_string) # /login/?error=The username & password combination are incorrect - please try again!
return redirect(url) # redirects to the login page with an error-message
login.html
<!-- error message -->
<div id="error" class="alert alert-danger alert-dismissible" role="alert">
×
{{ view.error }}
</div>
<form method="post" action="{% url 'custom_login' %}">
{% csrf_token %}
{% for field in form %}
<div class="form-group row">
{{ field.errors }}
<label for="{{ field.name }}" class="col-md-4 col-form-label text-md-right">{{ field.label }}</label>
<div class="col-md-6">
{{ field }}
</div>
</div>
{% endfor %}
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
Login
</button>
</div>
</form>
You don't need to manually collect, add to context, and display errors. The form itself stores the errors. You're already getting field-specific errors with your call to field.errors in the template, but you can also get non-field-specific errors with form.errors. See the Django docs on form errors for more details.
As an aside, you can display GET variables from the url in your templates. Simply use {{ request.GET.error }}. Again, there is no need to encode your error message in the url itself. It's not a good way to solve this problem, which Django has already solved for you.
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)
...
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)
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 some issues with validation.When i am submitting the form, the form.is_valid():
always returns false.
After modifying the form i.e. after removing all validations from the form, it still return the false(my form is not submitted)
the code of html
{% extends "base.html" %}
{% block extrahead %}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" type="text/javascript"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.min.js" type="text/javascript"></script>
<script>
$(function() {
$( "#id_birthday" ).datepicker();
});
</script>
{% endblock %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<div class="register_div">
<p><label for="username">Username:</label></p>
<p>{{ form.username }}</p>
</div>
<div class="register_div">
<p><label for="email">Email:</label></p>
<p>{{ form.email }}</p>
</div>
<div class="register_div">
<p><label for="password">Password:</label></p>
<p>{{ form.password }}</p>
</div>
<div class="register_div">
<p><label for="password1">Verify Password:</label></p>
<p>{{ form.password1 }}</p>
</div>
<div class="register_div">
<p><label for="birthday">Birthday:</label></p>
<p>{{ form.birthday }}</p>
</div>
<div class="register_div">
<p><label for="name">Name:</label></p>
<p>{{ form.username }}</p>
</div>
<p><input type="submit" value="submit" alt="register"/></p>
</form>
{% endblock %}
the code of forms.py
from django import forms
from django.contrib.auth.models import User
from django.forms import ModelForm
from drinker.models import Drinker
class RegistrationForm(ModelForm):
username = forms.CharField(label=(u'User Name'))
email = forms.EmailField(label=(u'Email Address'))
password = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False))
password1 = forms.CharField(label=(u'Verify Password'), widget=forms.PasswordInput(render_value=False))
class Meta:
model=Drinker
exclude=('user',)
def clean_username(self):
username=self.cleaned_data['username']
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError("The Username is already taken, please try another.")
def clean_password(self):
password=self.cleaned_data['password']
password1=self.cleaned_data['password1']
if password1 != password:
raise forms.ValidationError("The Password did not match, please try again.")
return password
class LoginForm(forms.Form):
username = forms.CharField(label=(u'User Name'))
password = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False))
and the code of views.py
def DrinkerRegistration(request):
if request.user.is_authenticated():
return HttpResponseRedirect('/profile/')
if request.method == 'POST':
form = RegistrationForm(request.POST)
#return render_to_response('register')
if form.is_valid():
user=User.objects.create_user(username=form.cleaned_data['username'], email=form.cleaned_data['email'], password = form.cleaned_data['password'])
user.save()
# drinker=user.get_profile()
# drinker.name=form.cleaned_data['name']
# drinker.birthday=form.cleaned_data['birthday']
# drinker.save()
drinker=Drinker(user=user,name=form.cleaned_data['name'],birthday=form.cleaned_data['birthday'])
drinker.save()
return HttpResponseRedirect('/profile/')
else:
return render_to_response('register.html',{'form':form} , context_instance=RequestContext(request))
else:
''' user is not submitting the form, show them a blank registration form '''
form = RegistrationForm()
context={'form':form}
return render_to_response('register.html',context , context_instance=RequestContext(request))
the model code
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class Drinker(models.Model):
user =models.OneToOneField(User)
birthday =models.DateField()
name =models.CharField(max_length=100)
def __unicode__(self):
return self.name
#create our user object to attach to our drinker object
def create_drinker_user_callback(sender, instance, **kwargs):
drinker, new=Drinker.objects.get_or_create(user=instance)
post_save.connect(create_drinker_user_callback, User)
I can see two possible reasons:
Your template contains two usages of username: once at the top and once at the bottom. The form might fail because this does not fit into a single CharField.
It's hard to say without your model class, but maybe it contains fields you haven't explicitly excluded?
If it's not option 1, could you post your Drinker model as well?
Did you checked the errors reporting while validating?
use print form.errors and check your console for error messages
If your code changes aren't reflecting in the run environment, look at removing your *.pyc files. These sometimes can get cached.
For linux OS you can run the following from the root of your project;
find . -iname "*.pyc" -exec rm -f {} \;