見出し画像

[Lightsail Django]No9 認証機能を使う(django-allauth)

django-allauthを使用すると、ログイン画面、サインアップ画面、パスワード変更画面などの認証機能が利用できるようになります。

ユーザー情報の管理としてDjango標準ではUserモデルを使用しますが、今後拡張できるように、カスタマイズ版のユーザーモデルの作成(CustomUser)して、それでユーザー情報を管理していきます。

1. pipでdjango-allauthをインストール

以下は、Bitnamiパッケージでpipインストールする際に、インストール先のディレクトリには注意が必要ですよという記事です。pipインストールでうまくいかなかった方は見てみてください。

1-1. Djangoのインストール先を確認する

Location項目がライブラリのインストール先なので、後続で実施するdjango-allauthのインストールも同ディレクトリを指定する

pip show Django
DEPRECATION: Loading egg at /opt/bitnami/python/lib/python3.11/site-packages/pip-23.3.1-py3.11.egg is deprecated. pip 23.3 will enforce this behaviour change. A possible replacement is to use pip for package installation..
Name: Django
Version: 4.2.7
Summary: A high-level Python web framework that encourages rapid development and clean, pragmatic design.
Home-page: https://www.djangoproject.com/
Author: Django Software Foundation
Author-email: foundation@djangoproject.com
License: BSD-3-Clause
Location: /opt/bitnami/python/lib/python3.11/site-packages
Requires: asgiref, sqlparse
Required-by: django-allauth

1-2. ライブラリのディレクトリに書き込み権限を付与

ls -ltr /opt/bitnami/python/lib/python3.11/ | grep site-packages
drwxr-xr-x 27 root root   4096 Nov 15 18:13 site-packages
sudo chmod 777 /opt/bitnami/python/lib/python3.11/site-packages
ls -ltr /opt/bitnami/python/lib/python3.11/ | grep site-packages
drwxrwxrwx 27 root root   4096 Nov 15 18:13 site-packages

1-3. ディレクトリを指定しdjango-allauthをインストール

pip install django-allauth -t /opt/bitnami/python/lib/python3.11/site-packages

2.アプリを作成

CustomUserモデルを作成するためにアプリを作ります。そのアプリ内のmodels.pyにCustomUserモデルを作成して行きます。

 #python  manage.py startapp [任意のアプリ名]
python manage.py startapp accounts

3.CustomUserの作成

作成したアプリのmodels.pyにCustomUserの作成して行きます。

# /opt/bitnami/projects/[プロジェクト名]/[アプリ名]/models.py
/opt/bitnami/projects/TEST/accounts/models.py
from django.db import models
from django.contrib.auth.models import UserManager, AbstractUser

class CustomUserManager(UserManager):
    pass

class CustomUser(AbstractUser):
    objects = CustomUserManager()

    def __str__(self):
        return self.email

後続で出てくるAbstractUserや、UserManagerのcreate_user、create_superuserメソッドについては以下でまとめています。

4.CustomUserクラス

4-1.AbstractUserを継承させる

今回はAbstractUserを継承させた形でCustomUserクラスを作成しました。
AbstractUserにはプロフィール項目が多く用意されており、デフォルトのUserモデルからも継承されています。

プロフィール項目が数が少ない状態からカスタマイズしていく場合は、AbstractBaseUser,PermissionsMixinを継承させます。

継承関係は以下。
CustomUser - AbstractUser - AbstractBaseUser,PermissionsMixin

4-2.独自項目を追加する場合はCustomUserクラス内に追加する

ユーザーモデルをカスタマイズすると言いつつも、上記例では特に独自項目も追加していないため、結果的にカラムのラインナップはUserモデルを使用しているのと同じとなります。

独自項目を追加する場合は、以下AbstractUserのusernameやfirst_nameのように、必要に応じて追加して行きます。ユーザー情報なので、住所や電話番号などですかね?

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username and password are required. Other fields are optional.
    """

    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _("username"),
        max_length=150,
        unique=True,
        help_text=_(
            "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
        ),
        validators=[username_validator],
        error_messages={
            "unique": _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_("first name"), max_length=150, blank=True)
    last_name = models.CharField(_("last name"), max_length=150, blank=True)
    email = models.EmailField(_("email address"), blank=True)
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site."),
    )
    is_active = models.BooleanField(
        _("active"),
        default=True,
        help_text=_(
            "Designates whether this user should be treated as active. "
            "Unselect this instead of deleting accounts."
        ),
    )
    date_joined = models.DateTimeField(_("date joined"), default=timezone.now)

    objects = UserManager()

    EMAIL_FIELD = "email"
    USERNAME_FIELD = "username"
    REQUIRED_FIELDS = ["email"]
# ....

4-3.CustomUserManagerをコールする


5.CustomUserManager

5-1.UserManagerを継承する

今回はUserManagerを継承させた形でCustomUserManagerクラスを作成しました。
UserManagerには、create_user、create_superuserメソッドそれぞれのis_staff、is_superuserの設定処理があり、この処理をこのまま使用したいのと、CustomUserでAbstractUserを継承しているので(AbstractUserで扱う項目に対応しているので)でこれを継承しました。

BaseUserManagerを継承する方法もあり、CustomUserでAbstractUserを継承せず、独自項目で構成される場合などは、その独自項目に沿った加工処理や設定処理が必要になってくるかと思うので、利用する項目によって適宜変更する形になるかと思います。

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, username, email, password, **extra_fields):
        """
        Create and save a user with the given username, email, and password.
        """
        if not username:
            raise ValueError("The given username must be set")
        email = self.normalize_email(email)
        # Lookup the real model class from the global app registry so this
        # manager method can be used in migrations. This is fine because
        # managers are by definition working on the real model.
        GlobalUserModel = apps.get_model(
            self.model._meta.app_label, self.model._meta.object_name
        )
        username = GlobalUserModel.normalize_username(username)
        user = self.model(username=username, email=email, **extra_fields)
        user.password = make_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, username, email=None, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", False)
        extra_fields.setdefault("is_superuser", False)
        return self._create_user(username, email, password, **extra_fields)

    def create_superuser(self, username, email=None, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)

        if extra_fields.get("is_staff") is not True:
            raise ValueError("Superuser must have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")

        return self._create_user(username, email, password, **extra_fields)

5-2.独自項目に関する加工処理などを追加する場合はCustomUserManagerクラス内に追加する

上記例ではCustomUserで特に独自項目も追加していないため、CustomUserManager側も特に独自項目に対応した加工処理などはありません。

6.settings.pyの修正

6-1.INSTALLED_APPS

今回作成したアプリを追加(accounts)と、allauth用に、 'django.contrib.sites', 'allauth', 'allauth.account', 'allauth.socialaccount',を追加して行きます。

INSTALLED_APPS = [
    # ...
    'accounts'
    'django.contrib.sites',
    'allauth', 
    'allauth.account',
    'allauth.socialaccount',
]

6-2.AUTHENTICATION_BACKENDS、MIDDLEWARE

以下の通り設定して行きます。

AUTHENTICATION_BACKENDS = [
	# ...
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend', # for allauth
]
MIDDLEWARE = [
	# ...
    'allauth.account.middleware.AccountMiddleware', # for allauth
]

6-3.SITE_IDを追加

SITE_ID とは Djangoプロジェクトの識別値で、この設定値がないとエラーとなります。

SITE_ID = 1
django.contrib.sites.models.Site.DoesNotExist: Site matching query does not exist

6-4.認証やサインアップ時の制御を設定

# メールアドレス(とパスワードで)認証する
ACCOUNT_AUTHENTICATION_METHOD = 'email' 

# サインアップ(ユーザー登録)の時にユーザーネームを尋ねる
ACCOUNT_USERNAME_REQUIRED = True 

# サインアップ(ユーザー登録)の時にメールアドレスを尋ねる
ACCOUNT_EMAIL_REQUIRED = True 

# サインアップ時にメール検証を必須とする'mandatory' 必須としない'none' 
#ACCOUNT_EMAIL_VERIFICATION = 'mandatory' 
ACCOUNT_EMAIL_VERIFICATION = 'none' 

6-5.URL周りの設定

ここは自分のアプリケーションの構成にあった通りのURLを設定していきます。
またログイン、ログアウトのURLについては、後続で出てくるurls.pyの設定にも関係してきます。

# ログインURLの設定
LOGIN_URL = '/account/login/' 

# ログイン後のリダイレクト先
LOGIN_REDIRECT_URL = '/APP_TEST/' 

# ログアウト後のリダイレクト先
ACCOUNT_LOGOUT_REDIRECT_URL = '/account/login/' 

6-6.AUTH_USER_MODELにCustomUserを追加

AUTH_USER_MODELに、カスタマイズして作成したユーザーモデルを追加します。こうすることでデフォルトのUserモデルではなくCustomUserが使用される用になります。
設定値としては、上記で作成したCustomUserモデルを指定します。

AUTH_USER_MODEL = "accounts.CustomUser" # allauth

6-7.おまけ(Userモデルの参照)

ちなみに、Userモデルは以下で参照でき、AUTH_USER_MODELに設定があるとAUTH_USER_MODELに設定されたモデル、ない場合はデフォルトのUserモデルが参照されるようです。

settings.AUTH_USER_MODEL
get_user_model() # django.contrib.auth

7.プロジェクトのurls.pyを修正

プロジェクトのurls.pyを修正します。(アプリのurls.pyは修正不要)
第一引数の値はなんでも良いですが、'account/〜'でリクエストされたらaullauthライブラリ側の処理をします。

path('account/', include('allauth.urls')),

補足:allauth/account/urls.py
(allauth/urls.pyで、allauth/account/urls.pyをコール)

urlpatterns = [
    path("signup/", views.signup, name="account_signup"),
    path("login/", views.login, name="account_login"),
    path("logout/", views.logout, name="account_logout"),
    path("reauthenticate/", views.reauthenticate, name="account_reauthenticate"),
    path(
        "password/change/",
        views.password_change,
        name="account_change_password",
    ),
    path("password/set/", views.password_set, name="account_set_password"),
    path("inactive/", views.account_inactive, name="account_inactive"),
# ....

8.マイグレーション

8-1.マイグレーションファイルを生成する

python manage.py makemigrations accounts

8-2.マイグレーションする

python manage.py migrate

9.画面表示

以上で設定は終了ですので、最後に画面を表示します!
http://[IPアドレス]/account/login/

10.その他

その他、amazon LightsailにてDjangoを使用したWEBアプリ構築については以下となります。(こちらの記事は以下記事の続きとなります。)


この記事が気に入ったらサポートをしてみませんか?