Django, show ValidationError in template

69,939

Solution 1

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.

Solution 2

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? <a href="{% url 'login' %}">Login</a></p>

    </form>

** Feel free to copy code and enjoy! **

Solution 3

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/

Share:
69,939

Related videos on Youtube

manosim
Author by

manosim

Updated on April 29, 2022

Comments

  • manosim
    manosim about 2 years

    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>
                <a href="../../">Return to the homepage.</a><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 %}
    
  • manosim
    manosim over 10 years
    Thanks for your quick reply! The thing is that I can't manage the "ValidationError" to show the message in the template. i.e.({% if form.errors %}...)
  • antimatter
    antimatter over 10 years
    Ah it's in the django docs. You can do form.errors and others if you use form.is_valid() in your view. See my updated response.
  • antimatter
    antimatter over 10 years
    It should show in {% form.errors %} if you check the validation in your view using form.is_valid(). I'm curious, so keep me posted if it works.
  • manosim
    manosim over 10 years
    The thing is that I do use the "form.is_valid()" in my views.py file. You can find it here pastebin.com/068mAnWh .
  • antimatter
    antimatter over 10 years
    I found the answer here: link I updated my response. You need to call add_error() and non_field_errors()
  • manosim
    manosim over 10 years
    To be honest I tried it but couldn't figure out where to place it in my code. Tried to find some examples that use it too but nothing. Any chance you could help me further?
  • antimatter
    antimatter over 10 years
    Try placing those in the view and save it in a variable. Then pass it in as a variable in your context when you run render(request, 'url', context), and try printing out the variable. Let me know how it goes.
  • manosim
    manosim over 10 years
    Hmm. I did this link and also added {{ form.non_field_errors }} but still nothing.
  • antimatter
    antimatter over 10 years
    Change form.non_field_errors() to something like custom_error = form.non_field_errors(), then create something like args['custom']=custom_error and access {% custom %}
  • manosim
    manosim over 10 years
    Well I tried but unfortunately had no results. I think I have to find an alternative so will look for some tutorials or for an app for that. Thanks a lot for your help!! Really appreciate it!
  • antimatter
    antimatter over 10 years
    Ah of well :/ A quick fix would just be to pass in the custom error as a message in your context when you render. Or to use flash messages...
  • Valachio
    Valachio over 6 years
    I'm having the same error but I don't understand how args play into this. Why does args even exist? It is not used in the template.