Reputation: 41
My forms are not showing the error messages when I try to submit an empty form but I can see the errors while looping over the field errors in views.py
. How do I overcome this problem?
the template
(Updated):
{% block formcontent %}
{{form.non_field_errors}}
<div class="row">
<div class="col">
{{form.username.label_tag}} {{form.username}} {{form.username.errors|striptags}}
</div>
</div><br>
<div class="row">
<div class="col">
{{form.first_name.label_tag}} {{form.first_name}} {{form.first_name.errors|striptags}}
</div>
<div class="col">
{{form.last_name.label_tag}} {{form.last_name}} {{form.last_name.errors|striptags}}
</div>
</div><br>
<div class="row">
<div class="col">
{{form.email.label_tag}} {{form.email}} {{form.email.errors|striptags}}
</div>
</div><br>
<div class="row">
<div class="col">
{{form.location.label_tag}} {{form.location}} {{form.location.errors|striptags}}
</div>
<div class="col">
{{form.designation.label_tag}} {{form.designation}} {{form.designation.errors|striptags}}
</div>
</div><br>
<div class="row">
<div class="col">
{{form.password1.label_tag}} {{form.password1}} {{form.password1.errors|striptags}}
</div>
<div class="col">
{{form.password2.label_tag}} {{form.password2}} {{form.password2.errors|striptags}}
</div>
</div><br>
{% endblock formcontent %}
Edit 1: (Updated)
class MyRegistrationForm(UserCreationForm):
password1=forms.CharField(label='Password', widget=forms.PasswordInput(attrs={'class':'form-control'}))
password2=forms.CharField(label='Confirm Password', widget=forms.PasswordInput(attrs={'class':'form-control'}))
class Meta:
model=MyRegistration
fields=['username', 'first_name', 'last_name', 'email', 'location', 'designation']
widgets={
'username':forms.TextInput(attrs={'class':'form-control'}),
'first_name':forms.TextInput(attrs={'class':'form-control'}),
'last_name':forms.TextInput(attrs={'class':'form-control'}),
'email':forms.EmailInput(attrs={'class':'form-control'}),
'location':forms.Select(attrs={'class':'form-select'}),
'designation':forms.TextInput(attrs={'class':'form-control'}),
}
def clean_username(self):
username = self.cleaned_data.get('username')
if not username:
raise ValidationError('Username is required!')
else:
try:
MyRegistration.objects.get(username=username)
raise ValidationError('This username already exists!', code='username_exists')
except MyRegistration.DoesNotExist:
pass
return username
def clean_email(self):
email=self.cleaned_data.get('email')
if not email:
raise ValidationError('Email is required!')
else:
try:
MyRegistration.objects.get(email=email)
raise ValidationError('This email already exists!', code='email_exists')
except MyRegistration.DoesNotExist:
pass
return email
def clean_first_name(self):
first_name=self.cleaned_data.get('first_name')
if not first_name:
raise ValidationError('First-name is required!')
return first_name
def clean_last_name(self):
last_name=self.cleaned_data.get('last_name')
if not last_name:
raise ValidationError('Last-name is required!')
return last_name
def clean_location(self):
location=self.cleaned_data.get('location')
if not location:
raise ValidationError('Location is required!')
return location
def clean_designation(self):
designation=self.cleaned_data.get('designation')
if not designation:
raise ValidationError('Designation is required!')
return designation
I really have no idea what is wrong with my codes in template. I have checked, the Django documentation suggests the same way to approach such scenarios where the forms are not looped over.
Edit 2:
models.py:
class MyRegistration(AbstractBaseUser, PermissionsMixin):
location_list=[
('Solapur', 'Solapur'),
('Dhule', 'Dhule'),
('Other', 'Other'),
]
username=models.CharField(max_length=10, unique=True)
email=models.EmailField(unique=True)
first_name=models.CharField(max_length=150)
last_name=models.CharField(max_length=150)
location=models.CharField(max_length=10, choices=location_list, default=None)
designation=models.CharField(max_length=70)
is_active=models.BooleanField()
is_staff=models.BooleanField(default=False)
start_date=models.DateTimeField(default=timezone.now)
last_login=models.DateTimeField(null=True)
USERNAME_FIELD='username'
REQUIRED_FIELDS=['email', 'first_name', 'last_name', 'location', 'designation']
objects=FirstManager()
def __str__(self):
return self.first_name
views.py:(Updated)
def signup(request):
print('1')
if request.user.is_authenticated:
print('2')
if request.method=='POST':
print('3')
if request.POST.get('password1')==request.POST.get('password2'):
print('4')
fm=MyRegistrationForm(request.POST)
for field in fm:
print("Field Error:", field.name, field.errors)
if fm.is_valid():
print('6')
fm.save()
messages.success(request, 'Registered successfully!!')
fm=MyRegistrationForm()
print('7')
cur_user=request.user
return render(request, 'account/signup.html', {'form':fm, 'cur_user':cur_user})
else:
fm=MyRegistrationForm()
cur_user=request.user
return render(request, 'account/signup.html', {'form':fm, 'cur_user':cur_user})
else:
return HttpResponseRedirect('/')
Upvotes: 1
Views: 131
Reputation: 1879
When you raise ValidationError
in the clean
method, these errors get added to the non_field_errors
attribute on the form. This is why nothing gets rendered when using form.email.errors
and other errors
attributes on particular fields.
You should render the form.non_field_errors
before you render your form, so you can see those errors, too.
However, to solve your issue, I would rather go with the option of splitting the validation of each field into particular methods clean_<field_name>
. For example for username field:
def clean_username(self):
username = self.cleaned_data.get('username')
if not username:
raise ValidationError('Username is required!')
else:
try:
un=MyRegistration.objects.get(username=self.instance.username)
raise ValidationError('This username already exists!')
except MyRegistration.DoesNotExist:
pass
# Make sure you return the value of the data in
# the clean_<field_name> methods
return username
And so on for other fields, too. Doing just this should fix your code, but here are some other recommendations:
ValidationErrors
. E.g.: raise ValidationError('This username already exists', code='username_exists')
EDIT #1:
The use of instance
to get the values from the submitted form is wrong. Since this form is used exclusively for create purposes as registration is creation of a new user, instance
will always be empty. instance
is only filled with data when you're updating a model instance.
You should replace the uses of instance
with getting the form data from self.cleaned_data
dict.
For example:
# Instead of:
username = self.instance.username
# Use:
username = self.cleaned_data.get('username')
EDIT #2: After the author added the view code.
The issue might be in your view code. Also, there is no need to comparing password1
and password2
as the UserCreationForm
already does that for your.
The core issue is that if your form is invalid, you need to re-render that same form, not create another instance. I propose the following update:
def signup(request):
print('1')
if request.user.is_authenticated:
print('2')
if request.method=='POST':
print('3')
form = MyRegistrationForm(request.POST)
if form.is_valid():
print('4')
form.save()
messages.success(request, 'Registered successfully!!')
# If you do this, you always render the empty form
# without the errors
# fm=MyRegistrationForm()
print('7')
cur_user=request.user
return render(
request, 'account/signup.html',
{'form': form, 'cur_user':cur_user}
)
else:
form = MyRegistrationForm()
cur_user=request.user
return render(
request, 'account/signup.html',
{'form':form, 'cur_user':cur_user}
)
else:
return HttpResponseRedirect('/')
Some other recommendations:
@login_required
decorator works better for this.render
for success scenarios, only when handling the GET method or when you need to re-render the form to display validation errors.Upvotes: 1