I make login/password form:
model:
class LoginForm(forms.Form):
username = forms.CharField(max_length=100)
password = forms.CharField(widget=forms.PasswordInput(render_value=False),max_length=100)
view:
def login_view(request):
if request.method == 'POST':
username = request.POST['email']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user is not None and user.is_active:
login(request, user)
return HttpResponseRedirect("/n1.html")# Redirect to a success page.
return HttpResponseRedirect("/login")
form=LoginForm()
return render(request, 'enter.html', {'login_form': LoginForm})
urls:
(r'^login/$', login_view),
template:
{% if form.errors %}
<p>Something is wrong</p>
{% endif %}
<form class="form-signin" action="" method="post">
<h2 class="form-signin-heading">Login</h2>
{% csrf_token %}
<input class="input-block-level" type="text" name="email" value="" id="email" placeholder="Email">
<input class="input-block-level" type="password" name="password" value="" id="username" placeholder="Password">
<button class="btn btn-large btn-primary" type="submit">Login</button>
<input type="hidden" name="next" value="{{next|escape}}" />
</form>
I use redirect to login page then login or password is wrong, but I want to make error message in this case. Why construction {% if form.errors %} doesn't work? Thx!
Because the form has no idea an error occurred.
When you construct the form here:
form=LoginForm()
You're constructing it without passing it any information. It doesn't know anything about the POST the user just did, or that the the login failed, or that the password was missing, or whatever the error was.
Here's what my login forms look like:
class LoginForm(forms.Form):
username = forms.CharField(max_length=255, required=True)
password = forms.CharField(widget=forms.PasswordInput, required=True)
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
user = authenticate(username=username, password=password)
if not user or not user.is_active:
raise forms.ValidationError("Sorry, that login was invalid. Please try again.")
return self.cleaned_data
def login(self, request):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
user = authenticate(username=username, password=password)
return user
We override the form's clean method, so that if the form passes validation we can check the user's credentials. We also put a login method on the form object itself, to make our view cleaner.
Next, in our view, we want to do this:
def login_view(request):
form = LoginForm(request.POST or None)
if request.POST and form.is_valid():
user = form.login(request)
if user:
login(request, user)
return HttpResponseRedirect("/n1.html")# Redirect to a success page.
return render(request, 'enter.html', {'login_form': form })
We instantiate the form and hand it the request.POST to check against.
If we have a POST, we check if the form is valid. The form's "clean" method will get called, and check the user credentials for us.
If the credentials fail, we raise an error, which we need to show in our template.
Errors raised by the form (but not attached to a field) are stored in non_field_errors, which can be displayed like so:
{% if form.non_field_errors %}
<ul class='form-errors'>
{% for error in form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
When you redirect, I'm not sure you send the context of your page. So when you redirect to the /login/ page, django tinks it's a new form which is loaded.
Related
I set a user.is_active to false so they can't login.
user.is_active = False
user.save()
I would like to override the login section to show that the account has been disabled. Currently it shows on disabled accounts.
Please enter a correct username and password. Note that both fields may be case-sensitive.
I am using the auth login:
path('accounts/', include('django.contrib.auth.urls')),
With a simple template:
{% extends 'base.html' %}
{% block title %}Login{% endblock %}
{% block content %}
<h2>Log In</h2>
<form method="POST" action="."enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Log In</button>
<button>Sign up</button>
</form>
{% endblock %}
I have seen something like where they override clean and call this function.
def confirm_login_allowed(self, user):
if not user.is_active:
raise forms.ValidationError(
"This account has been disabled",
code='inactive',
)
from django.contrib import messages
from django.contrib.auth.forms import AuthenticationForm
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request.POST)
username = request.POST['username']
password = request.POST['password']
user = authenticate(username=username, password=password)
if user:
if user.is_active:
auth_login(request, user)
return redirect('home')
else:
messages.error(request,'User blocked')
return redirect('login')
else:
messages.error(request,'username or password not correct')
return redirect('login')
else:
form = AuthenticationForm()
return render(request, 'registration/login.html',{'form':form})
Just had to check is_active then send messages.error after overriding login.
I have a class based view which shows a login-form.
The problem is that I can't display error messages. I am trying to send an error message in a parameter in the URL to display it inside the HTML template file. But it does not work.
Here is my code so far:
forms.py
# a class which act as a view - it displays the login-form
class LoginForm(AuthenticationForm, BaseLoginView):
username=forms.CharField(widget=forms.TextInput(attrs={'class':'form-control'}))
password=forms.CharField(widget=forms.PasswordInput(attrs={'class':'form-control'}))
def get_context_data(self, **kwargs):
context = super(LoginForm, self).get_context_data(**kwargs)
context['error'] = ''
return context
urls.py
urlpatterns = [
path('login/', views_auth.LoginView.as_view(form_class=LoginForm, redirect_authenticated_user=True), name='login'), # login-page
]
views.py
# login functionality for the user
def custom_user_login(request):
if request.method == 'GET':
error_message = ''
return redirect('home')
elif request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
error_message = ''
# if the username & password is correct
user = authenticate(request, username=username, password=password)
if user is not None:
# Redirecting to the required login according to user type (admin / regular-user)
if user.is_superuser or user.is_staff:
login(request, user)
return redirect('admin_section/')
else:
login(request, user)
return redirect('/')
# display error message
else:
base_url = reverse('login') # /login/
query_string = urlencode({'error': 'The username & password combination are incorrect - please try again!'}) # error=The username & password combination are incorrect - please try again!
url = '{}?{}'.format(base_url, query_string) # /login/?error=The username & password combination are incorrect - please try again!
return redirect(url) # redirects to the login page with an error-message
login.html
<!-- error message -->
<div id="error" class="alert alert-danger alert-dismissible" role="alert">
×
{{ view.error }}
</div>
<form method="post" action="{% url 'custom_login' %}">
{% csrf_token %}
{% for field in form %}
<div class="form-group row">
{{ field.errors }}
<label for="{{ field.name }}" class="col-md-4 col-form-label text-md-right">{{ field.label }}</label>
<div class="col-md-6">
{{ field }}
</div>
</div>
{% endfor %}
<div class="col-md-6 offset-md-4">
<button type="submit" class="btn btn-primary">
Login
</button>
</div>
</form>
You don't need to manually collect, add to context, and display errors. The form itself stores the errors. You're already getting field-specific errors with your call to field.errors in the template, but you can also get non-field-specific errors with form.errors. See the Django docs on form errors for more details.
As an aside, you can display GET variables from the url in your templates. Simply use {{ request.GET.error }}. Again, there is no need to encode your error message in the url itself. It's not a good way to solve this problem, which Django has already solved for you.
I'm creating an app in which I'd like to use my own custom login form with a captcha field. My intention is to do this without using an external library (except for requests) but I couldn't add captcha field to my custom form in forms.py, so I added it directly to login.html but for some reason when I do form.is_valid() it returns an error.
I've already seen the solutions in Django - adding google recaptcha v2 to login form and Adding a Recaptcha form to my Django login page but as I said, I'd like to do this without using an external library.
views.py
...
def login_view(request):
if request.method == 'POST':
form = CustomLoginForm(request.POST)
result = is_recaptcha_valid(request)
print(result) # prints True
if form.is_valid():
username = form.cleaned_data['username']
email = form.cleaned_data['email']
password = form.cleaned_data['password']
user = authenticate(request, username=username, password=password)
if user is not None:
login(request, user)
# Redirect to index
messages.success(request, "Logged in.")
return HttpResponseRedirect(reverse('orders:index'))
else:
messages.error(request, "Invalid credentials.")
else:
print("error")
return render(request, 'registration/login.html', {'form': CustomLoginForm()})
else:
form = CustomLoginForm()
return render(request, 'registration/login.html', {'form': form})
forms.py
class CustomLoginForm(AuthenticationForm):
email = forms.EmailField(
error_messages={
'required': 'Please enter your email.',
'invalid': 'Enter a valid email address.'
},
help_text='Email',
)
login.html
<form class="" action="{% url 'orders:login' %}" method="post">
{% csrf_token %}
{% for field in form %}
<p>
{{ field.label_tag }}<br>
{{ field }}
{% if field.help_text %}
<small style="color: grey">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<p style="color: red">{{ error }}</p>
{% endfor %}
</p>
{% endfor %}
<!-- ReCAPTCHAV3 -->
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<div class="g-recaptcha" data-sitekey='key-here'></div>
<button class="btn btn-success" type="submit" name="">Login</button>
<!-- <input type="hidden" name="next" value="{{ next }}"> -->
</form>
is_recaptcha_valid() function already returns True so I didn't share that. I'm a beginner in Django, so if you can please explain in two words what I've done wrong instead of just posting the answer, I'd be grateful. Thank you for your time.
The AuthenticationForm is slightly different than the others..
If your check AuthenticationForm class, AuthenticationForm 's first arguments is not data like others form:
class AuthenticationForm(forms.Form):
...
def __init__(self, request=None, *args, **kwargs):
...
Thats why you need to pass request.POST to data.
So update your code like this:
def login_view(request):
if request.method == 'POST':
form = CustomLoginForm(data=request.POST)
...
problem with my assignment....
I also need to make users login based on whether their role (usertype) is 'reader' or 'client' to be redirected to the proper welcome page. Plus i want to use my custom model (User's username & password) for login credentials. I have read the django docs custom auth but i still don't know i will implement it into my project.
models.py
class User(models.Model):
id = models.AutoField(primary_key=True, unique=True)
title = models.CharField(max_length='10')
surname = models.CharField(max_length='50')
firstname = models.CharField(max_length='50')
username = models.CharField(max_length='50', unique=True)
password = models.CharField(max_length='50')
email = models.EmailField(max_length='50')
phone = models.BigIntegerField(max_length='12')
city = models.CharField(max_length='50')
country = models.CharField(max_length='50')
usertype = models.CharField(max_length=13)
views.py
def login(request):
c = {}
c.update(csrf(request))
return render_to_response('login.html', c)
def auth_view(request):
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=username, password=password)
if user is not None:
auth.login(request, user)
return HttpResponseRedirect('adminwelcome')
else:
return HttpResponseRedirect('invalid')
template
{% extends "main.html" %}
{% block title %}Log In - {{ block.super }}{% endblock %}
{% block content %}
{% if form.errors %}
<p>Sorry, that is not a valid username or password</p>
{% endif %}
<form action = "auth_view" method="post"> {% csrf_token %}
<label for="username">Username:</label>
<input type="text" name="username" value="" id="username" />
<br />
<label for="password">Password:</label>
<input type="password" name="password" value="" id="password" />
<br />
<input type="submit" value="Login" />
</form>
<p>Not Registered? Create Account </p>
{% endblock %}
What i did was that when my User model form is being saved, it should get the username, password and email and create a user in the Django auth User table using this line of code
User.objects._create_user(request.POST['username'], request.POST['email'], request.POST['password'],False,False)
This is the full code in the views.py
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
form.save()
User.objects._create_user(request.POST['username'], request.POST['email'], request.POST['password'],False,False)
# Redirect to the document list after POST
return HttpResponseRedirect('register_success')
else:
form = RegisterForm()
args = {}
args.update(csrf(request))
args['forms'] = RegisterForm()
return render_to_response('register.html', args)
Then for the login based on roles, I had to make the user a staff from the admin end by setting the 'is_staff' = true. After that, all i had to do was pass the 'is_staff==True' in the auth_view request during login authentication. A bit dirty but it does the trick. Any further optimization is appreciated. Cheers.
Views.py
def auth_view(request):
username1 = request.POST.get('username', '')
password1 = request.POST.get('password', '')
user = auth.authenticate(username=username1, password=password1)
#check if user has been set to staff (reader), redirect to reader welcome
if user is not None and user.is_staff==True:
auth.login(request, user)
return HttpResponseRedirect('readerwelcome')
#check if user is not staff(client), redirect to client welcome
elif user is not None:
auth.login(request, user)
return HttpResponseRedirect('clientwelcome')
#if login details not found, return error page
else:
return HttpResponseRedirect('invalid')
I have a simple signup form (in signup.html)
<form action="adduser" method="post">
{% csrf_token %}
Email Address: <input type="email" name="email" required autocomplete="on" placeholder="fr#star.com"/><br/>
Username: <input type="text" name="username" maxlength=25 required placeholder="JoyfulSophia"/><br/>
Password: <input type="password" name="password" maxlength=30 required placeholder="**********" /><br/>
<br/>
<input type="submit" value="Send" /> <input type="reset">
</form>
This redirects to the addUser view:
def adduser(request):
u = User.objects.create_user(request.POST['username'], request.POST['email'], password=request.POST['password'])
u.save()
a = Accounts(user=u)
p = Passwords(user=u)
a.save()
p.save()
return HttpResponseRedirect(reverse('OmniCloud_App.views.profile', args=(u.id,)))
Here is the profile:
#login_required
def profile(request, User_id):
u = get_object_or_404(User, pk=User_id)
a = get_object_or_404(Accounts, pk=User_id)
return render_to_response('profile.html', context_instance=RequestContext(request))
So they wouldn't be signed in, but that's okay because we can send you over to /accounts/login?next=/7/ since they are user 7 (Problems Ahead!)
def login(request):
username = request.POST['username']
password = request.POST['password']
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
auth.login(request, user)
return HttpResponseRedirect("/account/profile/")
else:
return HttpResponseRedirect("/account/invalid/")
The request doesn't contain anything called username, but the one which was submitted to the addUser form does, so how can I shoot that bro over to login? I could have it parse the url (which contains the next=USER_ID) but that wouldn't work for someone that just types in base_url/login, and the user_id won't be part of the url forever. So what's a brother to do?
Post data exists only for one request. If you want to use it later you should save it somewhere else.
You could login the user right after registration, in adduser view, he just entered his username and password, he doesn't have to do it again.
And login view is a little off. This is just a "POST" part of the view. You need to check and see if it's GET request and if it is return template with form containing username and password fields and with target url that points to the same view. Something like this:
def login(request):
if request.method == 'GET':
return render_to_response('login.html',
{ 'form': LoginForm() },
context_instance=RequestContext(request))
elif request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user = auth.authenticate(username=username, password=password)
if user is not None and user.is_active:
auth.login(request, user)
return HttpResponseRedirect("/account/profile")
else:
return HttpResponseRedirect("/account/invalid/")
Where login.html is something like this:
{% extends "base_site.html" %}
{% block content %}
<form method="post" target="{% url login_view %}">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Login" />
</form>
{% endblock content %}
Also, you could return user to the same login form if username and password didn't match and add some message!
This is just from the top of my head, didn't try it, but it should work!
There is an extensible user-registration application for Django called django-registration that offers you a lot of functionality for creating and registering users. Setting it up is very simple, you can find a very good doc here