user1950164
user1950164

Reputation:

Django form.is_valid() always false

I'm coding a login. When I programmed the form by hand I got it working.

The code below works:

views.py

def login_view(request):
    if request.method == 'GET':
        return render(request, 'app/login.htm')
    if request.method == 'POST':
        username = request.POST.get('username', '')
        password = request.POST.get('password', '')
        user = auth.authenticate(username=username, password=password)
        if user is None:
            return HttpResponseRedirect(reverse('error'))
        if not user.is_active:
            return HttpResponseRedirect(reverse('error'))

        # Correct password, and the user is marked "active"
        auth.login(request, user)
        # Redirect to a success page.
        return HttpResponseRedirect(reverse('home'))

template:

<form method="post" action="{% url 'login'  %}">
    {% csrf_token %}
    <p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="30" /></p>
    <p><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></p>

    <input type="submit" value="Log in" />
    <input type="hidden" name="next" value="" />
</form>

Great! But now I want to do the same thing using Django's forms.

The code below is not working because I get is_valid() == False, always.

views.py:

def login_view(request):
    if request.method == 'POST':
        form = AuthenticationForm(request.POST)
        print form.is_valid(), form.errors, type(form.errors)
        if form.is_valid():
            ## some code....
            return HttpResponseRedirect(reverse('home'))
        else:
            return HttpResponseRedirect(reverse('error'))
    else:
        form = AuthenticationForm()
    return render(request, 'app/login.htm', {'form':form})

template:

<form action="{% url 'login' %}" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />

There are a bunch of people on stackoverflow complaining that they get is_valid always false. I have read all those posts, and as far as I can tell I'm not making any of those mistakes. I found a new mistake to make :-)

EDIT: I added a print in the code. The output when opening the login view and submitting is

[27/Dec/2013 14:01:35] "GET /app/login/ HTTP/1.1" 200 910
False  <class 'django.forms.util.ErrorDict'>
[27/Dec/2013 14:01:38] "POST /app/login/ HTTP/1.1" 200 910

and so is_valid() is False, but form.errors is empty.

Upvotes: 17

Views: 16582

Answers (3)

Arindam Roychowdhury
Arindam Roychowdhury

Reputation: 6511

If situation arises, that you don't have an option (I was trying to work with bootstrap modals and it was just not working), I had to do this, or else the modal would always trigger even if the form had not issues (and the is_valid is always False by default)

What I needed:

  • Show modal when I click a button
  • if errors, show on the same page, the modal, with the error.

In the modal template:

{% if not brand_form.is_valid and brand_form.errors %}
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
        <script type="text/javascript">
             $(window).on('load', (function() {
                $('#brandAddModal').modal('show');
             }));
        </script>
                    
          {{ brand_form.non_field_errors }}
{% endif %}

In the view:

def add_brand_form(request):
    form = BrandForm()
    if request.method == 'POST':
        form = BrandForm(data=request.POST)
        if form.is_valid():
            return HttpResponseRedirect('/home')
        else:
            return render(request, template_name='home.html', context={'brand_form':form})
        
    return render(request, template_name='modal_add_brand.html', context={'brand_form':form})

Upvotes: 0

Zhuo.M
Zhuo.M

Reputation: 476

Check out form.errors which will help you find out why.

Upvotes: 12

Daniel Roseman
Daniel Roseman

Reputation: 599600

It turns out that Maxime was right after all (sorry) - you do need the data parameter:

form = AuthenticationForm(data=request.POST)

The reason for that, though, is that AuthenticationForm overwrites the signature of __init__ to expect the request as the first positional parameter. If you explicitly supply data as a kwarg, it will work.

(You should still leave out the else clause that redirects away on error, though: it's best practice to let the form re-render itself with errors in that case.)

Upvotes: 19

Related Questions