I create a registation app, where users can register providing a username, email and a password. What I did is make sure that the email field is unique(as you can see in the code below). But I can't figure out how I can show the error in case the a user enters an email address that is already in use.
View
from django.shortcuts import render
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.core.context_processors import csrf
from forms import RegistrationForm
# Create your views here.
def register_user(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('../../membership/register_success')
else:
return HttpResponseRedirect('../../membership/register_failed')
args = {}
args.update(csrf(request))
args['form'] = RegistrationForm()
return render(request,'registration/registration_form.html', args)
def register_success(request):
return render_to_response('registration/registration_success.html')
def register_failed(request):
return render_to_response('registration/registration_failed.html')
Form
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from django.utils.translation import ugettext_lazy as _
# forms.py
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ('username', 'email', 'password1', 'password2')
def clean_email(self):
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
if email and User.objects.filter(email=email).exclude(username=username).count():
raise forms.ValidationError(_("This email address is already in use. Please supply a different email address."))
return email
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
registration.html
{% extends "base.html" %}
{% block title %}Registration{% endblock %}
{% block content %}
<h1>Registration</h1>
{% if form.errors %}
<h1>ERRORRRRRR same email again???</h1>
{% endif %}
{% if registered %}
<strong>thank you for registering!</strong>
Return to the homepage.<br />
{% else %}
<strong>register here!</strong><br />
<form method="post" action="/membership/register/">{% csrf_token %}
{{ form }}
<input type="submit" name="submit" value="Register" />
</form>
{% endif %}
{% endblock %}
You're showing the form with {{ form }} on the template. That itself should show all the validation errors by default, but in your case, you're redirecting to some other page if the form is invalid. So you can never show the errors unless you pass the errors with the GET parameters. You could change your view to this to get the errors on the signup page itself -
def register_user(request):
args = {}
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('../../membership/register_success')
else:
form = RegistrationForm()
args['form'] = form
return render(request,'registration/registration_form.html', args)
How this works is, if the request method is POST, the form gets initiated with the POST data, then it's validated with the is_valid() call, so the form object now has the validation error messages if it's invalid. If it's valid, it's saved and redirected. If not valid, it comes to the args['form'] = form part where the form object with the error messages is set to the context and then passed to render.
If the request method is not POST, then a form object with no data is instantiated and passed to render().
Now your template should show all the error messages just below each field if there is any error.
forms.py
from django import forms
class RegistForm(forms.Form):
name = forms.CharField(required=True)
email = forms.EmailField(required=True)
password = forms.CharField(required=True)
views.py
from django.shortcuts import render
from django.views.generic import TemplateView
import forms
class Register(TemplateView):
def get(self, request):
return render(request, 'register.html', {})
def post(self, request):
form = forms.RegistForm(request.POST)
if form.is_valid():
print(1)
else:
print(form.errors)
content = {'form':form};
return render(request, 'register.html', content)
register.html
<form action="{% url 'register' %}" method="post">
{% csrf_token %}
<fieldset>
<label for="name">Name:</label>
<input type="text" id="name" name="name" value="">
{{ form.errors.name }}
<label for="mail">Email:</label>
<input type="text" id="mail" name="email">
{{ form.errors.email }}
<label for="password">Password:</label>
<input type="password" id="password" name="password">
{{ form.errors.password }}
</fieldset>
<button type="submit">Sign Up</button>
<p class="message">Already registered? Login</p>
</form>
** Feel free to copy code and enjoy! **
Why not just do something like this:
...
if User.objects.filter(email=email):
raise forms.ValidationError(_("This email address is already in use. Please supply a different email address."))
return email
...
If the user already registered, have it raise a validation error. If you don't want it to do this, you can do something like:
...
email_exists = User.objects.filter(email=email):
if email_exists and email_exists.username != username:
raise forms.ValidationError(_("This email address is already in use. Please supply a different email address."))
return email
...
To display the form errors, you use form.is_valid() to make sure that it passes validation. Django says the following for custom validations:
Note that any errors raised by your Form.clean() override will not be associated with any field in particular. They go into a special “field” (called __all__), which you can access via the non_field_errors() method if you need to. If you want to attach errors to a specific field in the form, you need to call add_error().
Then in your template you can use something like {{ form.non_field_errors }}, etc.
See this section in the Django docs, under Using a form in a view and Customizing the form template:
https://docs.djangoproject.com/en/dev/topics/forms/
Class based views are easier.
from django.views import generic
from .forms import RegistrationForm
class RegistrationView(generic.CreateView):
template_class = 'registration/registration_form.html'
form_class = RegistrationForm
success_url = '/success/url'
def form_valid(self, form):
# add a log after save or whatever
super(RegistrationView, self).form_valid(self, form)
The clean methods are automatic with forms and messages render as such,
class based views make life a lot easier and your code DRYer.
Have resolved this error by the following:
from django.core.exceptions import ValidationError
try:
except ValidationError as e:
Related
I am new to Django. I am trying to make a simple form to match the password. However, when I enter different passwords and press the Save button I get a cleared form instead of showing the validation error.
Here newuser.html:
{% block content %}
<form method="POST">
{% csrf_token %}
<table>
{{frmNewUser.as_table}}
{% for error in frmNewUser.password.errors %} {% comment %} I tried frmNewUser.non_field_errors too {% endcomment %}
<p>{{error}}</p>
{% endfor %}
</table>
<input type="submit" name="Save" value="Save" colspan=2>
</form>
{% endblock content %}
Here forms.py:
class NewUserFrom(forms.Form):
username = forms.CharField(max_length=50, widget=forms.TextInput)
password = forms.CharField(widget=forms.PasswordInput)
confirm_password = forms.CharField(label="Confirm password", widget=forms.PasswordInput)
name = forms.CharField(max_length=50, widget=forms.TextInput)
email = forms.EmailField(max_length=50, widget=forms.EmailInput)
def clean(self):
cleaned_data = super().clean()
pwd = cleaned_data.get('password')
cof_pwd = cleaned_data.get('confirm_password')
if pwd and cof_pwd:
if pwd != cof_pwd:
raise forms.ValidationError('Password is not match.')
return super().clean()
Here views.py:
from django.shortcuts import render
from django.http import HttpResponse, request
from django.db import connection
from django.contrib.auth.decorators import login_required
import pyodbc
from .forms import NewUserFrom
def newUser(request):
form = NewUserFrom(request.POST)
if not form.is_valid():
return render(request,'login/newuser.html', {'frmNewUser':NewUserFrom})
return render(request, "login/welcome.html")
Try following this article, it helped when i had a similar problem
in the newUser function
def newUser(request):
form = NewUserFrom(request.POST)
if not form.is_valid():
return render(request,'login/newuser.html', {'frmNewUser':NewUserFrom})
return render(request, "login/welcome.html")
the return value in the if statement should be changed from {'frmNewUser':NewUserFrom}) to {'frmNewUser':form})
Before extending the user module, I was easily able to register new users and the page would get redirected to the login page. Now on registering new users, the profile is not getting created and the page is also not getting redirected. The new user does get created though and sometimes the error is object does not exist, user profile does not exist, and sometimes the error is forbidden, csrf verification failed. I dont know where I'm going wrong. existing users are able to login and update profiles but new users I'm having a problem with.
Models.py is:
from django.db import models
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User,null= True ,on_delete= models.CASCADE)
profile_pic = models.ImageField(null = True, blank= True)
first = models.CharField(max_length=500, null=True)
last = models.CharField(max_length=500, null=True)
email = models.CharField(max_length=500, null=True)
mobile_number = models.IntegerField(null=True)
location = models.CharField(max_length= 500, null= True)
postal = models.IntegerField(null=True)
def __str__(self):
return self.first
My forms.py is:
from django.forms import ModelForm, fields
from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.contrib.auth.models import User
from .models import *
class CreateUserForm(UserCreationForm):
email = forms.EmailField()
password2 = None
class Meta:
model = User
fields = ['username','first_name', 'last_name','email', 'password1']
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = '__all__'
exclude = ['user']
widgets = {
'profile_pic': forms.FileInput()
}
views.py is (I removed the login and logout view cause that was working fine):
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from .forms import CreateUserForm, ProfileForm
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib import messages
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.decorators import login_required
from .models import *
def RegisterPage(request):
if request.user.is_authenticated:
return redirect('Profile')
else:
if request.method == 'POST':
form = CreateUserForm(request.POST)
if form.is_valid():
user = form.save()
name = form.cleaned_data.get('first_name')
messages.success(request, 'Account created for ' + name)
Profile.object.create(
user = user,
)
Profile.save()
return HttpResponseRedirect('/Login/')
else:
form = CreateUserForm()
context = {'form':form}
return render(request, 'register.html', context)
#login_required(login_url='Login')
def Profile(request):
profile = request.user.profile
form = ProfileForm(instance=profile)
if request.method == 'POST':
form = ProfileForm(request.POST, request.FILES, instance=profile)
if form.is_valid():
form.save()
context = {'form': form}
return render(request, 'profile.html', context)
my register template:
<form class="" action="" method="post">
{% csrf_token %}
<p class="reg-field-title"><strong>Username*</strong></p>
<div class="forms">{{form.username}}</div>
<p class="reg-field-title"><strong>First Name*</strong></p>
<div class="forms">{{form.first_name}}</div>
<p class="reg-field-title"><strong>Last Name*</strong></p>
<div class="forms">{{form.last_name}}</div>
<p class="reg-field-title"><strong>Email-ID*</strong></p>
<div class="forms">{{form.email}}</div>
<p class="reg-field-title"><strong>Password*</strong></p>
<div class="forms">{{form.password1}}</div>
<button type="submit" class="btn btn-dark btn-lg col-lg-10 reg-btn">Register</button>
</form>
My login template:
<p class="login-reg">New to MedsPlain? <a class="log-reg-link" href="/Registration/">Register </a>here</p>
<hr> {% if next %}
<form class="" action='/Login/Profile/' method="post"> {% csrf_token %} {%else%}
<form class="" action="/Login/" method="post">
{% endif %} {% csrf_token %}
<p class="login-field-title"><strong>Username*</strong></p>
<input type="text" name="username" class="form-control col-lg-10 log-inp-field" placeholder="Enter Username" required>
<p class="login-field-title"><strong>Password*</strong></p>
<input type="password" name="password" class="form-control col-lg-10 log-inp-field" placeholder="Enter Password" required> {% for message in messages %}
<p id="messages">{{message}}</p>
{% endfor %}
<button type="submit" class="btn btn-dark btn-lg col-lg-10 log-btn">Log In</button>
</form>
I've tried everything as of now, but i don't understand the mistake. Can someone please guide me through cause at this moment i'm frustrated, on the verge of crying and don't understand what to do.
There are basically two problems here:
there is a view Profile, and thus this will override the reference to the Profile model; and
you do not create a model record with Model.object.create(), but with Model.objects.create().
I would advise that you rename your views in snake_case, and remove the wildcard import, this is often not a good idea:
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from .forms import CreateUserForm, ProfileForm
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib import messages
from django.contrib.auth import login, authenticate, logout
from django.contrib.auth.decorators import login_required
from .models import Profile
def register_page(request):
if request.user.is_authenticated:
return redirect('Profile')
else:
if request.method == 'POST':
form = CreateUserForm(request.POST)
if form.is_valid():
user = form.save()
Profile.objects.create(
user = user,
)
messages.success(request, 'Account created for {user.first_name}')
return HttpResponseRedirect('/Login/')
else:
form = CreateUserForm()
context = {'form': form }
return render(request, 'register.html', context)
#login_required(login_url='Login')
def profile(request):
profile = request.user.profile
form = ProfileForm(instance=profile)
if request.method == 'POST':
form = ProfileForm(request.POST, request.FILES, instance=profile)
if form.is_valid():
form.save()
context = {'form': form}
return render(request, 'profile.html', context)
You will also need to update the urls.py to work with the renamed views.
I have a Django project in which trying to 'update' (clicking on the button) causes the following error:
CSRF verification failed. Request aborted.
Help
Reason given for failure:
CSRF token missing or incorrect.
I have viewed various questions and answers that suggest that adding RequestContext is the solution, but I have tried something to that effect tailored to my code, and still am unable to get it working.
Original code views.py is below
#USERS (register) view.py
from django.shortcuts import render,redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib import messages #this allows us flash messages for testing
from django.contrib.auth.decorators import login_required
from .forms import UserRegisterForm
from .forms import UserUpdateForm
from .forms import ProfileUpdateForm
from django.contrib.auth.decorators import login_required
def register(request):
if request.method =='POST':
#this UserRegisterForm is created in forms.py and inherits from UserCreationForm (the ready made form)
form = UserRegisterForm(request.POST) #create a form that has the data that was in request.POST
if form.is_valid(): #is the form valid (do you have a username like this already, passwords match?
form.save()
username = form.cleaned_data.get('username')
messages.success(request,f'Account created for {username}, you can now login.')
return redirect('login')
else:
form =UserRegisterForm() #if the form input is invalid, render the empty form again
return render(request, 'users/register.html',{'form':form})
#login_required #this is a decorator (adds functionality to an existing function)
def profile(request):
if request.method =='POST':
u_form =UserUpdateForm(request.POST,instance=request.user)
p_form =ProfileUpdateForm(request.POST, request.FILES,instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request,f'Your Account has been directed')
return redirect('profile')
else:
u_form =UserUpdateForm(instance=request.user)
p_form =ProfileUpdateForm(instance=request.user.profile)
context={
'u_form': u_form,
'p_form': p_form
}
return render(request,'users/profile.html',context)
As mentioned I also tried:
return render(request,'users/profile.html',context_instance=RequestContext(request))
I also tried adding the below to imports
from django.shortcuts import get_object_or_404, render,redirect
Note: I do not wish to try the 'remove middleware' alternative.
The profile.html file code is below and I have included the CSRF token correctly, as far as I know:
{% extends "socialmedia/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<div class="media">
<img class="rounded-circle account-img" src="{{ user.profile.image.url }}">
<div class="media-body">
<h2 class="account-heading">{{ user.username }}</h2>
<p class="text-secondary">{{ user.email }}</p>
</div>
</div>
<form method="POST" enctype="multipart/form-data>
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Profile Information</legend>
{{u_form|crispy}}
{{p_form|crispy}}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Update....</button>
</div>
</form>
</div>
{% endblock content %}
Finally, for context, here is forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserRegisterForm(UserCreationForm): #form that inherits from the usercreationform
email = forms.EmailField()
class Meta:
model = User
#when this form validates it creates a new user
#type the fields to be shown on your form, in that order.
fields = ['username','email','password1','password2']
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username','email']
class ProfileUpdateForm(forms.ModelForm):
class Meta:
model= Profile
fields=['image']
Question: What is causing the error and what do I need to add/change in the code as it stands?
I also came across an answer on StackOverflow which said this "The problem is you are not returning the result. Remember that the view is a function. You need to return render_to_response(...) not just call it (which is also why by removing CSRF you got the didn't return an HttpResponse error)" ....can someone explain this in my context? I don't understand what it means or how to implement it either.
It looks like it should work, however I noticed you seem to be missing a quote at the end here
<form method="POST" enctype="multipart/form-data>
Everytime I submit my form on '/signup' view, form.validate_on_submit() in my views.py throws the error below:
TypeError: __init__() takes from 1 to 2 positional arguments but 3 were given
The stack trace is pretty long and I don't see anything immediately obvious. I have no idea why it is doing this. I followed the Flask-WTF docs for validating forms.
EDIT: Here is the stack trace I am seeing.
views.py
from myapp import app
from flask import render_template, redirect
from forms import RegistrationForm
#app.route('/', methods=['POST', 'GET'])
#app.route('/signup', methods=['POST', 'GET'])
def signup():
form = RegistrationForm()
if form.validate_on_submit():
# Redirect to Dash Board
return redirect('/dashboard')
return render_template("signup.html", form=form)
#app.route('/login')
def login():
return "<h1>Login</h1>"
#app.route('/dashboard')
def dashboard():
return "<h1>Dashboard</h1>"
forms.py
from flask_wtf import FlaskForm
from wtforms import TextField, PasswordField
from wtforms.validators import InputRequired, Email, Length
class RegistrationForm(FlaskForm):
username = TextField('username', validators=[InputRequired(), Length(min=4, max=30)])
email = TextField('email', validators=[InputRequired(), Email, Length(max=25)])
password = PasswordField('password', validators=[InputRequired(), Length(min=8, max=80)])
class LoginForm(FlaskForm):
username = TextField('username', validators=[InputRequired(), Length(min=4, max=30)])
password = PasswordField('password', validators=[InputRequired(), Length(min=8, max=80)])
signup.html
{% extends "base.html" %}
{% block content %}
<h1>Sign Up</h1>
<form method="POST" action="/signup">
{{ form.hidden_tag() }}
<p>Username:</p>
{{ form.username() }}
<p>Email:</p>
{{ form.email() }}
<p>Password:</p>
{{ form.password() }}
<br/>
<br/>
<button type="Submit" value="submit" name="submit">Submit</button>
</form>
{% endblock %}
I figured it out! In forms.py, my RegistrationForm's email attribute should read:
email = TextField('email', validators=[InputRequired(), Email(), Length(max=25)])
I forgot the darn parenthesis for the Email parameter.
Could you review my code for improvement? Essentially, it works but it isn't user friendly. For example,(1) If the user puts in an invalid field, it doesn't tell them what field is invalid. (2) If a user tries to register (sign up) with an existing username, there is no way the user can distinguishes that error, specifically. Or if a user mis-type password confirm, the user won't know that mistake either. (3) Also, if the user produces an input error(s), the sign up page blanks out all the field and so the user has to re-enter in all the info. As you can imagine, that could be very frustrating for that user.
I tried to see if form.error_messages might help the user, the error_messages is useless.
How would you improve the code so that (1), (2), (3) is not a nuisance for the user? And one more thing, just curious about how you would change the css? I noticed that most professional website sign-up page highlight the input box that was invalid. I am clueless on how to apply changes to css for {{form|as_bootstrap}}
Many thanks for your help!
form.py:
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
class MyRegistrationForm(UserCreationForm):
email = forms.EmailField(required=True,help_text="Email field is required.")
firstname = forms.CharField(max_length=50, required=False, help_text="Optional. Can fill in later.")
lastname = forms.CharField(max_length=50, required=False, help_text="Optional. Can fill in later.")
class Meta:
model = User
fields = (
'username',
'email',
'password1',
'password2',
'firstname',
'lastname'
)
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.email = self.cleaned_data["email"]
user.firstname = self.cleaned_data["firstname"]
user.lastname = self.cleaned_data["lastname"]
if commit:
user.save()
return user
views.py
from django.shortcuts import render, render_to_response
from django.http import HttpResponseRedirect
from django.contrib import auth
from django.core.context_processors import csrf
from forms import MyRegistrationForm
def register_user(request):
if request.method == 'POST':
form = MyRegistrationForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect('/account/register_success')
else:
form = MyRegistrationForm()
args = {}
args.update(csrf(request))
args['form']= form
print form.errors #this line doesn't print anything
return render_to_response('register.html', args)
else:
return HttpResponseRedirect('/')
def register_success(request):
return render_to_response('register_success.html')
register.html
{% extends 'base.html' %}
{% load bootstrap_toolkit %}
{
{% block title %}
<title>Register</title>
{% endblock %}
<body>
{% block content %}
<div class="col-sm-9 col-sm-offset-3 col-md-6 col-md-offset-2 main">
<h2>Register</h2>
{% if form.error_messages %}
<p class="error"> {{ form.error_messages }}.</p>
{% endif %}
<form class="form-signup" role="form" action="/account/register/" method="post"> {% csrf_token %}
{{ form|as_bootstrap }}
<input type="submit" value="Register" class="btn btn-primary"/>
</form>
</div>
{% endblock %}
</body>