🐍

【Python】django-allauthを用いて、ユーザーログイン機能を実装

2024/11/23に公開

達成目標

django-allauthを用いて、ユーザーログイン機能を実装できる。

ログイン画面


新規登録画面


ログイン後遷移画面

前提

  • アプリケーションのトップページが表示されている状態かつデータベースの設定が完了している状態から解説をしていく。

開発環境の構築がまだの方はこちらから↓

https://zenn.dev/code_journey_ys/articles/ddd8ba305a2538

1.requirements.txt ファイルの準備と編集

ファイルの作成

ターミナルで実行(作成済みの場合は飛ばす)
New-Item requirements.txt

ファイルの編集

requirements.txt
django==4.2
django-allauth==0.53.0 # 追加

モジュールをインストールコマンドを実行

docker-compose exec web pip install -r requirements.txt
出力結果
  Downloading django-allauth-0.53.0.tar.gz (735 kB)
     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 735.8/735.8 kB 2.7 MB/s eta 0:00:00      
  Preparing metadata (setup.py) ... done
Collecting python3-openid>=3.0.8 (from django-allauth==0.53.0->-r requirements.txt (line 33))

インストールされたかを確認

パッケージ一覧の確認コマンド
docker-compose exec web pip list
インストール済みのパッケージをrepuirement.txtに記録するコマンド
docker-compose exec web pip freeze > requirements.txt

2.settings.pyファイルの編集

settings.pyINSTALLED_APPSに追加

settings.pyファイルの編集①
INSTALLED_APPS = [
    # 既存のアプリ
    'django.contrib.sites',  # 必須
    'allauth',
    'allauth.account',
    'allauth.socialaccount',  # SNSログインを使用する場合
    'allauth.socialaccount.providers.google',  # 必要なSNSプロバイダを追加
]

SITE_ID = 1  # 必須

settings.pyINSTALLED_APPSに追加

settings.pyファイルの編集②
AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',  # Djangoのデフォルト認証
    'allauth.account.auth_backends.AuthenticationBackend',  # allauthの認証
]

settings.pyのその他設定を追加

settings.pyファイルの編集③
# ログイン後のリダイレクト先
LOGIN_URL = '/accounts/login/'
ACCOUNT_LOGOUT_REDIRECT_URL = '/'

# サインアップ時のメール確認機能を無効化(デフォルトはTrue)
ACCOUNT_EMAIL_VERIFICATION = "none"

# ユーザー名の使用を制御(Trueでユーザー名必須)
ACCOUNT_USERNAME_REQUIRED = True

# メールアドレスの使用設定
ACCOUNT_EMAIL_REQUIRED = True

3.urls.pyの編集

プロジェクトフォルダ内のurls.pyへ以下を追加
from django.views.generic import TemplateView
from allauth.account.views import LoginView, SignupView

class CustomLoginView(LoginView):
    template_name = 'account/login.html'
class CustomSignupView(SignupView):
    template_name = 'account/signup.html'

    path('accounts/', include('allauth.urls')),  # django-allauthのURL
    path('', TemplateView.as_view(template_name='home.html'), name='home'),  
    path('login/', CustomLoginView.as_view(), name='account_login'),
    path('signup/', CustomSignupView.as_view(), name='account_signup'),

4.テンプレートの作成

テンプレートフォルダとファイルの作成

ターミナルで実行
mkdir templates/account
New-Item templates/home.html
New-Item templates/account/login.html
New-Item templates/account/signup.html

テンプレートファイルの編集

home.htmlの中身
home.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Home</title>
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #f8f9fa;
            font-family: 'Arial', sans-serif;
        }
        .container {
            height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }
        .card {
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            border-radius: 10px;
            width: 100%;
            max-width: 30rem;
        }
        .btn-custom {
            background-color: #007bff;
            color: white;
            font-size: 16px;
            padding: 12px 24px;
            border-radius: 30px;
            border: none;
            transition: background-color 0.3s;
        }
        .btn-custom:hover {
            background-color: #0056b3;
        }
        .btn-custom:focus {
            outline: none;
        }
        h1 {
            font-size: 2.5rem;
            color: #333;
            text-align: center;
            margin-bottom: 30px;
        }
        .card-body {
            text-align: center;
            padding: 2rem;
        }
        .d-grid {
            gap: 15px;
        }
        .btn-danger {
            width: 100%;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="card">
            <div class="card-body">
                <h1>Welcome to Our Site</h1>
                <p class="lead">Please log in or sign up to continue</p>
                <div class="d-grid gap-3">
                    {% if user.is_authenticated %}
                        <p>ログインしています: {{ user.username }}</p>
                        <form action="{% url 'account_logout' %}" method="post">
                            {% csrf_token %}
                            <button type="submit" class="btn btn-danger">Logout</button>
                        </form>
                    {% else %}
                        <p>ログインしていません</p>
                        <a href="{% url 'account_login' %}" class="btn btn-primary">Login</a>
                        <a href="{% url 'account_signup' %}" class="btn btn-success">Sign Up</a>
                    {% endif %}
                </div>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
login.htmlの中身
login.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Site</title>
    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #f7f7f7;
            font-family: 'Arial', sans-serif;
        }
        .card {
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            width: 100%;
            max-width: 28rem;
        }
        .card-body {
            padding: 2rem;
        }
        .form-label {
            font-weight: bold;
            margin-bottom: 0.5rem;
        }
        .form-control {
            border-radius: 0.375rem;
            padding: 0.75rem 1.25rem;
            border: 1px solid #ccc;
        }
        .btn-custom {
            background-color: #007bff;
            color: #fff;
            border: none;
            border-radius: 0.375rem;
            padding: 0.75rem 1.25rem;
            font-size: 1rem;
            transition: background-color 0.3s;
        }
        .btn-custom:hover {
            background-color: #0056b3;
        }
        .text-muted {
            color: #6c757d;
        }
        .form-control:focus {
            border-color: #007bff;
            box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
        }
        .card-title {
            color: #343a40;
            margin-bottom: 1.5rem;
        }
        .text-primary {
            text-decoration: none;
        }
        .text-primary:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <header>
        <!-- ナビゲーションバーなど -->
    </header>

    <main class="container py-4">
        <div class="d-flex justify-content-center align-items-center vh-100">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h2 class="card-title text-center mb-4">Create an Account</h2>
                    <form method="post" class="mt-4">
                        {% csrf_token %}
                        {{ form.non_field_errors }}
                        {% for field in form %}
                            <div class="mb-3">
                                <label for="{{ field.id_for_label }}" class="form-label">{{ field.label }}</label>
                                {{ field }}
                                {% if field.errors %}
                                    <div class="text-danger small">{{ field.errors|striptags }}</div>
                                {% endif %}
                            </div>
                        {% endfor %}
                        <button type="submit" class="btn btn-custom w-100">Sign Up</button>
                    </form>
                    <p class="text-center mt-3">
                        Already have an account? <a href="{% url 'account_signup' %}" class="text-primary">Sign In</a>
                    </p>
                </div>
            </div>
        </div>
    </main>

    <!-- Bootstrap JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

signup.htmlの中身
signup.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sign In</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #f7f7f7;
            font-family: 'Arial', sans-serif;
        }
        .card {
            border-radius: 8px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            width: 100%;
            max-width: 28rem;
        }
        .card-body {
            padding: 2rem;
        }
        .form-label {
            font-weight: bold;
            margin-bottom: 0.5rem;
        }
        .form-control {
            border-radius: 0.375rem;
            padding: 0.75rem 1.25rem;
            border: 1px solid #ccc;
        }
        .btn-custom {
            background-color: #007bff;
            color: #fff;
            border: none;
            border-radius: 0.375rem;
            padding: 0.75rem 1.25rem;
            font-size: 1rem;
            transition: background-color 0.3s;
        }
        .btn-custom:hover {
            background-color: #0056b3;
        }
        .text-muted {
            color: #6c757d;
        }
        .form-control:focus {
            border-color: #007bff;
            box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
        }
        .card-title {
            color: #343a40;
            margin-bottom: 1.5rem;
        }
        .text-primary {
            text-decoration: none;
        }
        .text-primary:hover {
            text-decoration: underline;
        }
        .form-group {
            margin-bottom: 1rem;
        }
    </style>
</head>
<body>
    <div class="container py-4">
        <div class="d-flex justify-content-center align-items-center vh-100">
            <div class="card shadow-sm">
                <div class="card-body">
                    <h2 class="card-title text-center mb-4">Sign In</h2>
                    <form method="post" class="mt-4">
                        {% csrf_token %}
                        {{ form.non_field_errors }}
                        {% for field in form %}
                            <div class="form-group">
                                <label for="{{ field.id_for_label }}" class="form-label">{{ field.label }}</label>
                                {{ field }}
                                {% if field.errors %}
                                    <div class="text-danger small">{{ field.errors|striptags }}</div>
                                {% endif %}
                            </div>
                        {% endfor %}
                        <button type="submit" class="btn btn-custom w-100">Sign In</button>
                    </form>
                    <p class="text-center mt-3">
                        Don't have an account? <a href="{% url 'account_login' %}" class="text-primary">Login</a>
                    </p>
                </div>
            </div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

5.管理者ユーザーを作成

requirements.txtの内容が表示される。
docker-compose exec web python manage.py createsuperuser
出力結果
$ docker-compose exec web python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@example.com
Password: 
Password (again): 
Superuser created successfully.

6.画面遷移確認

http://localhost:8000/login/にアクセスし、管理者でログイン


ログイン画面

http://localhost:8000/へ遷移するかを確認


ログイン後遷移画面

Lgoutボタンを押して、http://localhost:8000/へ遷移するかを確認


ログインアウト後画面

Sign upボタンを押して、http://localhost:8000/signup/に遷移するか確認


新規登録画面

新規ユーザー登録を行ったあと、http://localhost:8000/へ遷移するかを確認


新規登録後画面

おまけ

ユーザー一覧を確認する方法

docker-compose exec web python manage.py shell -c "from django.contrib.auth.models import User; import json; print(json.dumps([{'username': u.username, 'email': u.email} for u in User.objects.all()], indent=4))"
出力結果
JSON形式で返してくれる
[
    {
        "username": "admin",
        "email": "admin@example.com"
    },
    {
        "username": "test1",
        "email": "test@test.com"
    }
]

Discussion