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.
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.)
Check out form.errors which will help you find out why.
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})
Related
I want to create a form in my navigation bar. I am using a context processor for that.
I created themeChoice field in my UserProfile model. What I want to is when user click submit button, form will be save. I tried something but it did not work, my view prints nothing. How can I do that?
Note: my context processor is working.
views.py
def approval_context_processor(request):
...
if request.method == 'POST':
form_theme = UserThemeChoiceform(request.POST)
if form_theme.is_valid():
user_who = request.POST.get('user_who', None)
user = UserProfile.objects.get(id=user_who)
print(user_who)
form_theme.save()
return redirect('home')
context= {
...
'form': form_theme,
}
return context
navigation.html
<form method="POST" id="theme" action="#">
{% csrf_token %}
<input type="hidden" name="user_who" value="{{ request.user }}" >
<button type="submit" class="btn btn-success">Send</button>
</form>
I want to use ready Django LoginView, but in the same time I have to use my own view for registration in same HTML template. If in urls.py file I will connect 2 views than i will connect only first. So my question is that how can use LoginView in my own view and use 2 forms with jinja?
here is my views.py file and html ;)
def index(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
formlog = auth_views.LoginView.as_view(template_name='main/index.html')
if 'signup' in request.POST:
if form.is_valid():
form.supervalid()
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Dear {username} you have been created a new accound!')
return redirect('main')
elif 'login' in request.POST:
if formlog.is_valid():
formlog.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Your account has been created! You are now able to log in')
return redirect('main')
else:
form = UserRegisterForm()
formlog = auth_views.LoginView.as_view(template_name='main/index.html')
return render(request, 'main/index.html', {'form': form, 'formlog': formlog})
# this code is not working , but not returning any errors
HTML
{% if not user.is_authenticated %}
<!-- Login -->
<div class="container">
<div class="log-1">
<div class="log-0">
<p class="logtitle">BareTalk</p>
<form method="POST" name="login">
{% csrf_token %}
{{ formlog|crispy }}
<button class="log-button first" type="submit">Login</button>
</form>
<button class="log-button second"
onclick="modalwindowops.open();" id="signup">Sign Up</button>
</div>
</div>
</div>
<!-- Signup -->
<div class="modal-overlay">
<div class="modal-window">
<span class="close-modal" onclick="modalwindowops.close();">×</span>
<form method="POST" name="signup">
<p>Sign Up</p>
{% csrf_token %}
{{ form|crispy }}
<button type="submit">Sign Up</button>
</form>
</div>
</div>
{% else %}
<h1>Welcome back Amigo!</h1>
{% endif %}
Neither if 'signup' in request.POST: nor elif 'login' in request.POST: is triggered in your index() view because your HTML forms do not actually contain those inputs. Note that the name attribute is deprecated for the <form> element.
Instead you can add a hidden <input> inside your forms, like this:
<form method="POST">
{% csrf_token %}
{{ formlog|crispy }}
<input type="hidden" name="login" value="true" />
<button class="log-button first" type="submit">Login</button>
</form>
Also,
formlog = auth_views.LoginView.as_view(template_name='main/index.html')
saves a view to formlog, not a form, so calling formlog.is_valid() will cause an error.
Instead of
elif 'login' in request.POST:
if formlog.is_valid():
formlog.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Your account has been created! You are now able to log in')
return redirect('main')
you probably will only need to do
elif 'login' in request.POST:
log_view = auth_views.LoginView.as_view(template_name='main/index.html')
log_view(request)
Calling is_valid(), save(), and doing the redirect is all done by LoginView already. If you want to still do the custom message.success() you will have to override one or more of the LoginView methods, but that is another topic.
Update:
You also need to change this line in the view: formlog = auth_views.LoginView.as_view(template_name='main/index.html') (before return render...) to:
formlog = AuthenticationForm(request)
Take this line outside of the else block.
Also add the import for the form at the top of your views.py:
from django.contrib.auth.forms import AuthenticationForm
This change is required because the template needs the form object (which is AuthenticationForm for LoginView by default) instead of the view object. The updated view function will look like:
from django.contrib.auth.forms import AuthenticationForm
def index(request):
if request.method == 'POST':
form = UserRegisterForm(request.POST)
if 'signup' in request.POST:
if form.is_valid():
form.supervalid()
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Dear {username} you have been created a new accound!')
return redirect('main')
elif 'login' in request.POST:
log_view = auth_views.LoginView.as_view(template_name='main/index.html')
log_view(request)
else:
form = UserRegisterForm()
formlog = AuthenticationForm(request)
return render(request, 'main/index.html', {'form': form, 'formlog': formlog})
Note that this can be improved by providing feedback when the login credentials are invalid. As it is, this updated code only reloads a blank login form if the provided credentials don't work.
I have in my template:
This is passed by {{form}}
<form action="" method="POST">
Inicio: <input type="text" id="start">
<input type="submit" value="Sned" >
{% csrf_token %}
</form>
Then in the views.py
def test(request):
if request.method != 'POST':
context = {'form': 'by GET'}
return render(request, 'test.html', context)
else:
if 'start' in request.POST:
start = request.POST['start']
else:
start = False
context = {'form': start}
return render(request, 'test.html', context)
It seems that always return False
If I dont check the existance of the key I have this error:
MultiValueDictKeyError
And the erropage says : "'start'" (single plus double quotes)
id is intended for javascript and css purposes. For variables that are important on server side, you should use name tag.
<input type="text" id="start" name="start">
You need to add a name attribute in your input, so when you are getting the POST data it will be found.
<form action="" method="POST">
Inicio: <input type="text" id="start" name="start">
<input type="submit" value="Sned" >
{% csrf_token %}
</form>
Also I recommend you to do the following change in your view:
Replace
request.POST['start']
by:
request.POST.get('start')
So, if the field is not found, it will be reassigned whith a None value.
add name
<input type="text" name="start" id="start">
I'm learning Django and have some troubles with forms. I try to create a simple form where I type a name and show it on another page. But is_valid() always returns false. Please, help me to find my error
forms.py
from django import forms
class OrderForm(forms.Form):
user=forms.CharField(max_length=100)
views.py
def order(request):
return render(request, 'myapp/order.html', {})
def contact(request):
username='not logged'
if request.method == 'POST' :
form=OrderForm(request.POST)
if form.is_valid():
username=form.cleaned_data['username']
else:
username='not worked'
else:
form=OrderForm()
return render(request, 'myapp/contacts.html', {'username':username})
order.html
<form name = "form" action = "{% url 'contact' %}" method = "POST" >
{% csrf_token %}
<input type="text" name="username" placeholder="Name">
<button type="submit">Login</button>
</form>
contacts.html
You are : <strong>{{ username }}</strong>
Your form control has the name username in HTML, while your form's field is named user in Django. Thus, nothing is set in the form field.
Normally you'd put the form into the context and then render it either as {{ form }} or the like, or render each field, rather than build your own form controls. The docs show how: https://docs.djangoproject.com/en/1.10/topics/forms/#working-with-form-templates
views.py
from forms import OrderForm
def order(request):
form = OrderForm()
return render(request, 'myapp/order.html', {"form" : form})
order.html
<form name = "form" action = "{% url 'contact' %}" method = "POST" >
{% csrf_token %}
{{form.as_p}}
<button type="submit">Login</button>
</form>
At the time of rendering template {{form.as_p}} looks like
<p><label for="id_username">Username:</label>
<input id="id_username" type="text" name="username" maxlength="100" required /></p>
I have a django login form and when I click sign it, it is supposed to show some errors if something is wrong, however it doesn't appear in the html if I do print form.errors it appears in the console, here is an example : <ul class="errorlist"><li>username<ul class="errorlist"><li>User does not exist!</li></ul></li></ul> and then instantly after that it displays"POST /signin HTTP/1.1" in the console. I was wondering, why does it print the HTML in the console if I ask it to and not display it on screen. I tried {{form.errors}} and {{form.non_field_errors}}.
View Code:
def login_view(request):
form = LoginForm(request.POST or None)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = authenticate(username=username, password=password)
login(request, user)
return HttpResponseRedirect('/tickets/createticket')
stuff = {'form': LoginForm}
print form.errors
return render_to_response('tickets/login.html', stuff, context_instance=RequestContext(request))
Html:
{%load staticfiles %}
<link rel="stylesheet" type="text/css" href="{% static 'tickets/signin.css' %}"/>
<h1>Please Sign in</h1>
<form method="POST" id="signin">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Sign in" />
</form>
The error is here. You are passing form class:
stuff = {'form': LoginForm}
You should pass the form instance which you tried to validate:
stuff = {'form': form}