\ BLACK FRYDAY! /

【django-allauth】accountsアプリでユーザー認証機能を集約する方法【カスタマイズ】

「ユーザー管理用のアプリ」を作ってユーザー認証機能を集約したい!

このような方に向けて書きました。

django-allauthを使えば認証機能をまるっと実装できるので、愛用されている方も多いのではないでしょうか?

ところがdjango-allauthは多機能ゆえにプロジェクトが散らかってしまいがち。

そこでおすすめするのが、ユーザー管理用のアプリを作成することです。

本記事ではaccountsアプリを作成してユーザー管理を行う方法をはじめ、これと関連したカスタマイズ方法も合わせてご紹介します。

accountsアプリで実装するための下準備

初めてでも進められるように、django-allauthのセットアップから解説します。

すでにdjango-allauthの環境が整っている方は、適宜読み飛ばしてください。

django-allauthの環境構築

django-allauthをインストールします。

pip install django-allauth

django-allauthを有効化するため、settings.pyに以下を記述します。

INSTALLED_APPS = [
    # 追加
    "django.contrib.sites",
    "allauth",
    "allauth.account",
    "allauth.socialaccount",
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "allauth.account.middleware.AccountMiddleware",  # 追加
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

AUTHENTICATION_BACKENDS = [
    "django.contrib.auth.backends.ModelBackend",
    "allauth.account.auth_backends.AuthenticationBackend",
]

SITE_ID = 1

プロジェクト側のurls.pyに次のコードを追加し、認証系のエンドポイントを一気に作成しましょう。

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    # django-allauthのエンドポイントを生成
    path("accounts/", include("allauth.urls")),
]

この一文により、次のエンドポイントが作成されます。

スクロールできます
エンドポイント名前空間
accounts/login/account_login
accounts/logout/account_logout
accounts/password/change/account_change_password
accounts/signup/account_signup
accounts/confirm-email/account_confirm_email
accounts/password/reset/account_confirm_email
accounts/password/reset/account_reset_password
accounts/password/reset/done/account_reset_password_done
accounts/password/reset/key/<uidb36>-<key>/account_reset_password_from_key
accounts/email/account_email
accounts/social/connections/socialaccount_connections
エンドポイントと名前空間

accountsアプリを作成

ユーザーを管理するaccountsアプリを作成します。

まずはアプリを格納するディレクトリを作成。

mkdir -p apps/accounts

コマンドでaccountsアプリを生成しましょう。

python manage.py startapp accounts apps/accounts

デフォルトだとurls.pyが生成されないので、作成しておきます。

touch apps/accounts/urls.py

Djangoにaccountsアプリを認識させるため、INSTALLED_APPSにアプリ名を追加。

INSTALLED_APPS = [
    # 追加
    "accounts.apps.AccountsConfig",
]

今回はappsディレクトリ配下にアプリを生成したので、apps.pyを修正しておきましょう。

from django.apps import AppConfig


class AccountsConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "apps.accounts"  # 元々は name = "accounts" だった

プロジェクトのurls.pyにはaccountsアプリへのルーティングを追加しておきましょう。

from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path("accounts/", include("accounts.urls")),  # 追加
    path("accounts/", include("allauth.urls")),
]

この際にはallauth.urlsより上に追加してください。

なぜなら、Djangoはurlpattersを上から読み込むので、先にallauth.urlsを書いてしまうと追加したエンドポイントが読み込まれることがないからです。

なお、accountsアプリは何も編集していないので、今の段階ではこのコード追加は無意味です。

後ほどご紹介するビューのカスタマイズなどで役に立ちます。

以上でaccountsアプリの基本設定は完了です。

カスタムユーザーモデルの作成

accountsアプリを作る目的は「ユーザー認証機能の集約」です。

その意味では、カスタムユーザーモデルを作ることはかなり重要な立ち位置であると言えます。

ここでは簡単に解説します。
カスタムユーザーについてもっと詳しく知りたい方は、次の記事も併せて読んでみてください。

» 参考:【Django】カスタムユーザーモデルの作り方【認証】

カスタムユーザーモデルを作成する

まずはmodels.pyでカスタムユーザーの定義を行います。

ここではシンプルにageフィールドのみを追加しました。

from django.contrib.auth.models import AbstractUser
from django.db import models


class CustomUser(AbstractUser):
    # 追加のフィールドを定義
    age = models.IntegerField(null=True, blank=True)

    def __str__(self):
        return self.username

ここではAbstractUserを継承しています。

そのため、AbstractUserで定義されているすべてのフィールドは保持されたままになります。

最後にDjangoでカスタムユーザーモデルを使うよう、settings.pyで指定します。

AUTH_USER_MODEL = "accounts.CustomUser"

カスタムユーザーに合わせたフォームを用意する

先ほどageフィールドを追加したので、フォームにもageフィールドとその保存ロジックを加えていきましょう。

from allauth.account.forms import SignupForm
from django import forms


class CustomSignupForm(SignupForm):
    age = forms.IntegerField(required=False)

    def save(self, request):
        user = super(CustomSignupForm, self).save(request)
        user.age = self.cleaned_data.get("age")
        user.save()
        return user

ここで継承しているSignupFormはdjango-allauth標準のサインアップフォームです。

つまりageフィールドは含まれていないので、ここで追加しています。

作成したサインアップフォームを有効化するため、settings.pyに以下を書き込みます。

ACCOUNT_FORMS = {"signup": "accounts.forms.CustomSignupForm"}

以上で簡単なカスタムユーザーモデルの作成は完了です。

その他のカスタマイズ

ユーザーモデル以外のカスタマイズ方法を解説します。

accountsアプリを作成する場合によく実装される内容ばかりなので、要件に応じて積極的に取り入れてみてください。

ビューをカスタマイズする方法

django-allauthで用意されている各エンドポイントに対して、ビューロジックを編集する方法をご紹介します。

django-allauthで用意されているビューを継承し、メソッドをオーバーライドします。

from allauth.account.views import SignupView


class CustomSignupView(SignupView):
    def form_valid(self, form):
        response = super().form_valid(form)
        # フォームが有効な場合の追加処理をここに記述
        return response

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 必要に応じて追加のコンテキストデータをここで設定
        context["extra_data"] = "Some extra data"
        return context

    def get_success_url(self):
        # サインアップ成功後のリダイレクト先をカスタマイズする場合はここで設定
        return super().get_success_url()

CustomSignupViewを有効化するため、アプリ側のurls.pyでルーティングを行いオーバーライドを有効にします。

from django.urls import path

from . import views

app_name = "accounts"

urlpatterns = [
    path("signup/", views.CustomSignupView.as_view(), name="signup"),
]

これで基本的なビューロジックのカスタマイズは完了です。

ビューロジックのカスタマイズについて詳しく知りたい方は、別記事をどうぞ。

» 参考:【django-allauth】ビューロジックのカスタマイズ方法

テンプレートHTMLのカスタマイズ

settings.pyDIRSで、DjangoにテンプレートHTMLのありかを教えてあげます。

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],  # プロジェクトレベルのテンプレートディレクトリ
        "APP_DIRS": True,
    },
]

さらに、テンプレートHTMLをカスタマイズする場合には「しかるべき場所」にオーバーライド用のHTMLファイルを配置する必要があります。

例えばサインアップページなら、以下に配置します。

templates/accounts/signup.html

project_root/
└── templates/
    └── accounts/
        └── signup.html

最後にビューで先ほどのカスタムHTMLを読むように設定しましょう。

こちらで説明したようにカスタムビューの作成とurls.pyの変更をします。

その上で、template_nameに自分で作ったテンプレートHTMLを指定します。

class CustomSignupView(SignupView):
    template_name = "accounts/custom_signup.html"

以上が基本的なテンプレートHTMLの作成方法です。

テンプレートHTMLのより詳しい内容は別記事で解説する予定です。(準備中)

【その他のテンプレートHTMLファイルを配置する場所】
project_root/
│
├── templates/
│   ├── base.html
│   │
│   ├── account/
│   │   ├── account_inactive.html
│   │   ├── email.html
│   │   ├── email_confirm.html
│   │   ├── login.html
│   │   ├── logout.html
│   │   ├── password_change.html
│   │   ├── password_reset.html
│   │   ├── password_reset_done.html
│   │   ├── password_reset_from_key.html
│   │   ├── password_reset_from_key_done.html
│   │   ├── password_set.html
│   │   ├── signup.html
│   │   ├── signup_closed.html
│   │   ├── verification_sent.html
│   │   └── verified_email_required.html
│   │
│   ├── socialaccount/
│   │   ├── authentication_error.html
│   │   ├── login_cancelled.html
│   │   ├── connections.html
│   │   ├── signup.html
│   │   └── snippets/
│   │       ├── provider_list.html
│   │       └── login_extra.html
│   │
│   └── custom_accounts/
│       └── profile.html
│
└── your_app/
    └── templates/
        └── your_app/
            ├── specific_template1.html
            └── specific_template2.html

アダプターを使ったカスタマイズ

アダプターは、django-allauthの基本的な動作をカスタマイズするのに使われます。

例えば次のようなことができます。

  • ユーザー作成時に追加のフィールドを処理する
  • ログイン成功時に特定のアクションを実行する
  • パスワード変更時のバリデーションルールを追加する

次の例は、「ユーザー作成時に追加のフィールドを処理する」ものです。

# accounts/adapters.py
from allauth.account.adapter import DefaultAccountAdapter


class CustomAccountAdapter(DefaultAccountAdapter):
    def save_user(self, request, user, form, commit=True):
        user = super().save_user(request, user, form, commit=False)
        user.custom_field = form.cleaned_data.get("custom_field")
        user.save()
        return user

なお、アダプターを有効化するにはsettings.pyでカスタムアダプターを指定する必要があります。

# settings.py
ACCOUNT_ADAPTER = "accounts.adapters.CustomAccountAdapter"

アダプターについての詳しい説明は、別記事に譲りたいと思います。(準備中)

シグナルを使ったカスタマイズ

シグナルは、特定のイベントで自動発火する関数です。

django-allauthでは、ユーザー登録やログインなどのイベントに対してシグナルが発生します。

例えばサインアップ時に追加処理を行いたい場合は以下の通りです。

# accounts/signals.py
from allauth.account.signals import user_signed_up
from django.dispatch import receiver


@receiver(user_signed_up)
def after_user_signed_up(request, user, **kwargs):
    # ユーザー登録後の追加処理
    user.profile.create()

ここで作成したsignals.pyはapps.pyでインポートして信号を接続します。

# accounts/apps.py
from django.apps import AppConfig


class AccountsConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "apps.accounts"

    def ready(self):
        import accounts.signals  # シグナルを読み込む

シグナルについて詳しくは別記事でご紹介する予定です。(準備中)

この記事が気に入ったら
フォローしてね!

シェア・記事の保存はこちら!

この記事を書いた人

karo@プログラマのアバター karo@プログラマ プログラマ

「書くことで人の役にたつ」をモットーに活動中。
本職はプログラマで、Pythonが得意。
基本情報技術者試験合格。

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)