Authentication I: Login, Logout, Signup
Introduction
Here comes authentication. Because it is a bit complicated, authentication will take two chapters: one for implementing the login, logout, and signup processes, and one for shifting our entire app behind these authentication requirements.
Auth views
In users/views.py
, we take advantage of the generic LoginView
, LogoutView
, and CreateView
to implement our authentication logic:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | from django.contrib.auth.views import LoginView, LogoutView
from django.views.generic import CreateView
from django.urls import reverse_lazy
from django.contrib.auth import get_user_model
class Login(LoginView):
template_name = "login.html"
next_page = reverse_lazy("home")
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect(self.next_page)
return super().get(request, *args, **kwargs)
class Logout(LogoutView):
next_page = reverse_lazy("home")
class SignUpView(CreateView):
model = get_user_model()
fields = ["username", "email", "password"]
template_name = "signup.html"
success_url = reverse_lazy("home")
def get(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect(self.success_url)
return super().get(request, *args, **kwargs)
|
We don't have to specify much to the generic views, they're quite full-featured as is. What we did here is indicate where the templates live and where the views redirect to (the defaults are accounts/profile
for LoginView
and None
for LogoutView
). We also overrode the get
method in LoginView
and SignUpView
, so that already authenticated users who for some reason visit the login page are automatically redirected to the home
URL. We didn't specify a template for LogoutView
because it's not necessary.
Auth urls
Let's deal with the URL patterns now. Create users/urls.py
and add the following:
| from django.urls import path
from .views import Login, Logout, SignUp
urlpatterns = [
path("login", Login.as_view(), name="login"),
path("logout", Logout.as_view(), name="logout"),
path("signup", SignUp.as_view(), name="signup"),
]
|
For every app that we create, we need to tell config/urls.py
to look at the patterns specified in the app's urls.py
file:
| urlpatterns = [
path("admin/", admin.site.urls),
path("", include("conduit.articles.urls")),
path("", include("conduit.users.urls")), # new
]
|
Auth templates
login.html
Let's create login.html
in the templates
folder:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 | {% extends 'base.html' %}
{% block title %}
<title>Sign in - Conduit: Django + HTMX</title>
{% endblock %}
{% block content %}
<div class="auth-page">
<div class="container page">
<div class="row">
<div class="col-md-6 offset-md-3 col-xs-12">
<h1 class="text-xs-center">Sign In</h1>
<p class="text-xs-center">
<a href="{% url 'signup' %}">Need an account?</a>
</p>
{{ form.non_field_errors }}
<form method="post">
{% csrf_token %}
<fieldset class="form-group">
<input
class="form-control form-control-lg"
type="email"
placeholder="Email"
name="{{ form.username.name }}"
>
{{ form.username.errors }}
</fieldset>
<fieldset class="form-group">
<input
class="form-control form-control-lg"
type="password"
placeholder="Password"
name="{{ form.password.name }}"
>
{{ form.password.errors }}
</fieldset>
<button class="btn btn-lg btn-primary pull-xs-right" type="submit">
Sign in
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
|
Notice that we are using form.username
to authenticate. I initially was trying to work with form.email
, because that was the field we chose to authenticate with, but it kept throwing errors: Django didn't see the field, didn't POST the value that I gave it, and asked for the username every time. It took me a while, but I realised that our username is the email. form.username
is effectively querying what the USERNAME_FIELD
is. Not straightforward though.
signup.html
Create signup.html
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54 | {% extends 'base.html' %}
{% block title %}
<title>Sign up - Conduit: Django + HTMX</title>
{% endblock %}
{% block content %}
<div class="auth-page">
<div class="container page">
<div class="row">
<div class="col-md-6 offset-md-3 col-xs-12">
<h1 class="text-xs-center">Sign up</h1>
<p class="text-xs-center">
<a href="{% url 'login' %}">Have an account?</a>
</p>
{{ form.non_field_errors }}
<form method="post">
{% csrf_token %}
<fieldset class="form-group">
<input
class="form-control form-control-lg"
type="text"
placeholder="Your {{ form.username.name }}"
name="{{ form.username.name }}"
value="{{ form.username.value|default_if_none:'' }}"
>
</fieldset>
{{ form.username.errors }}
<fieldset class="form-group">
<input
class="form-control form-control-lg"
type="email"
placeholder="Your {{ form.email.name }}"
name="{{ form.email.name }}"
value="{{ form.email.value|default_if_none:'' }}"
>
</fieldset>
{{ form.email.errors }}
<fieldset class="form-group">
<input
class="form-control form-control-lg"
type="password"
placeholder="Your {{ form.password.name }}"
name="{{ form.password.name }}"
>
</fieldset>
{{ form.password.errors }}
<button class="btn btn-lg btn-primary pull-xs-right">
Sign up
</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
|