Search code examples
pythondjangodjango-formsdjango-viewsdjango-registration

validating email in registration is not raising validationError in Django?


In my registration, I am enforcing that email-id is unique. That is if a user exists with that email-id, I want to raise a forms.ValidationError.

Here is my form.py:

from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User

class MyRegistrationForm(UserCreationForm):
    #define fields
    email=forms.EmailField(required=True)

    class Meta:
        model=User
        fields=('username','email','password1','password2')

    def clean_email(self):
            email = self.cleaned_data["email"]
            try:
                user = User.objects.get(email=email)
                print user.email
                print user.username
                raise forms.ValidationError("This email address already exists. Did you forget your password?")
            except User.DoesNotExist:
                return email

    def save(self, commit=True):
            user = super(MyRegistrationForm, self).save(commit=False)
            user.email=self.cleaned_data["email"]
            if commit:
                user.save()
            return user

my view.py:

def register_user(request):
    if request.method=='POST':
        form=MyRegistrationForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, "Thank you for signing up")
            return HttpResponseRedirect("/accounts/register_success")

    args={}
    args['form']=MyRegistrationForm()
    return render(request,"register.html",args)

when I register a user with same email id, it does not throw validationError. but simply return the register.html but no duplicate user is added.

why is it not throwing validationError?

I added a couple of print statements in forms.py (above) to print the username and email that I get from User.object.get(email=email) and it correctly prints the name and email id of previously registered user. so the control proceeds till there but it is not throwing validationError

EDIT:

I also tried creating a brand new user, but having passoword fields Not match. still, I do not get any error message. but it simply redirects to register.html

EDIT:

template/register.html

{% extends "base.html" %}

{% block content %}


<h2>Register</h2>
<form action="" method="post">{% csrf_token %}

{{ form.as_p }}

<input type="submit" value="register"/>

</form>
{% endblock %}

In this template, error gets displayed automatically right above the field where the error occurs. I don't have to do {{ form.email.error }}. However, I do not have a way to customize the error. eg bold or some css to them. Is that possible?


Solution

  • Django forms catch ValidationError exception and adds error to form _errors dict. This code from django BaseForm._clean_fields method:

            try:
                # ...
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self._errors[name] = self.error_class(e.messages)
                if name in self.cleaned_data:
                    del self.cleaned_data[name]
    

    So, by raising ValidationError exception you are telling django to add errors to cleaning field and make your form not valid.

    You can access field error in template:

    {{ form.field_name.errors }}
    

    and iterate one field errors (django docs Customizing the form template):

    {% if form.subject.errors %}
        <ol>
        {% for error in form.subject.errors %}
            <li><strong>{{ error|escape }}</strong></li>
        {% endfor %}
        </ol>
    {% endif %}
    

    and all fields errors (django docs Looping over the form’s fields):

    {% for field in form %}
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
    {% endfor %}
    

    and display non field errors:

     {{ form.non_field_errors }}
    

    update: you should add else block in view. Without else you are always create new form:

    def register_user(request):
        if request.method=='POST':
            form=MyRegistrationForm(request.POST)
            if form.is_valid():
                form.save()
                messages.success(request, "Thank you for signing up")
                return HttpResponseRedirect("/accounts/register_success")
        else:
            form = MyRegistrationForm()
    
        args={}
        args['form'] = form
        return render(request,"register.html",args)