Python Django 入門トップページ


カスタムユーザ認証

  1. プロジェクトの概要
  2. プロジェクトの作成と初期設定
  3. Users アプリケーションの作成と有効化
  4. 認証にカスタムユーザモデルを利用する
  5. モデルの作成
  6. マイグレーション
  7. ユーザの登録
  8. 管理ユーザの登録
  9. 管理サイトの作成
  10. Comments アプリケーションの作成
  11. ページ雛形の作成
  12. ログイン・ログアウトの実装
  13. Navbar の設置
  14. Comments アプリケーションのユーザ認証
  15. ユーザ一覧ページ
  16. ユーザ詳細情報の表示
  17. ユーザ情報の更新
  18. パスワードの変更
  19. Gmail 2段階認証の設定とアプリパスワードの取得
  20. メールの設定と送信
  21. パスワードのリセット
  22. ユーザ登録機能の実装
  23. ユーザ登録時に氏名も登録
  24. ユーザ登録時にメールアドレスも登録
  25. ユーザ登録してもログインできないように
  26. ユーザ登録後にメールを送信
  27. メール検証によるアカウントの有効化
  28. トークン有効期限の変更
  29. ログアウト後に top へリダイレクト
  30. 検証メールの再送信
  31. 未検証ユーザのログインエラーメッセージ
  32. メールに有効期限を表示
  33. フラッシュメッセージの変更
  34. 未検証ユーザのパスワードリセット

カスタムユーザ認証

未検証ユーザのログインエラーメッセージ

ここまでの作業の結果では,新規にユーザ登録をしてまだメールによるアカウント有効化を終えていないユーザがログインしようとしたときに,「ID とパスワードが異なります」というメッセージが表示されてしまいます.ここでは未検証ユーザのログインエラーメッセージを変更してみよう.

まず,ユーザ user_e の is_active を 0 に変更します.

sqlite> select id, username, is_active, email from users_user; ⏎
id|username|is_active|email
1|user_a|1|a@sample.com
2|user_b|1|b@sample.com
3|user_c|1|c@sample.com
4|root|1|root@example.com
7|user_e|1|e@sample.com
sqlite> update users_user set is_active = 0 where id = 7; ⏎
sqlite> select id, username, is_active, email from users_user; ⏎
id|username|is_active|email
1|user_a|1|a@sample.com
2|user_b|1|b@sample.com
3|user_c|1|c@sample.com
4|root|1|root@example.com
7|user_e|0|e@sample.com
sqlite>

アカウントが有効化された user_a のエラーメッセージです.

django2022-00359

アカウントの検証を終えていないユーザ user_e でも同じエラーメッセージが表示され,これは不親切かもしれません.

django2022-00360

エラーメッセージの処理は forms.py に記述します.

users/forms.py
class AuthenticationForm(forms.Form):
    """
    Base class for authenticating users. Extend this to get a form that accepts
    username/password logins.
    """

    username = UsernameField(widget=forms.TextInput(attrs={"autofocus": True}))
    password = forms.CharField(
        label=_("Password"),
        strip=False,
        widget=forms.PasswordInput(attrs={"autocomplete": "current-password", 'class': 'form-control'}),
    )

    error_messages = {
        "invalid_login": _(
            "Please enter a correct %(username)s and password. Note that both "
            "fields may be case-sensitive."
        ),
        "inactive": _("This account is inactive."),
    }

    def __init__(self, request=None, *args, **kwargs):
        """
        The 'request' parameter is set for custom auth use by subclasses.
        The form data comes in via the standard 'data' kwarg.
        """
        self.request = request
        self.user_cache = None
        super().__init__(*args, **kwargs)

        # Set the max length and label for the "username" field.
        self.username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
        username_max_length = self.username_field.max_length or 254
        self.fields["username"].max_length = username_max_length
        self.fields["username"].widget.attrs["maxlength"] = username_max_length
        if self.fields["username"].label is None:
            self.fields["username"].label = capfirst(self.username_field.verbose_name)

    def clean(self):
        username = self.cleaned_data.get("username")
        password = self.cleaned_data.get("password")

        if username is not None and password:
            # is_active が 0 のときに authenticate が None になってしまうため
            inactive_user = UserModel.objects.filter(username=username)
            if len(inactive_user) > 0:
                if not inactive_user[0].is_active:
                    self.add_error('username', 'ログインする前にメールの検証を行ってアカウントを有効化してください.')
                    self.add_error('username', 'ユーザ登録後にメールが届いているはずです.アカウントを有効化するにはそのメールの指示に従ってください.')
                    self.add_error('username', '検証メールの有効期限が切れた場合はユーザ登録ページからメールを再送信してください.')
                    raise ValidationError(
                        self.error_messages["inactive"],
                        code="inactive",
                    )

            self.user_cache = authenticate(
                self.request, username=username, password=password
            )
            if self.user_cache is None:
                raise self.get_invalid_login_error()
            else:
                self.confirm_login_allowed(self.user_cache)

        return self.cleaned_data

アカウントの検証を終えていないユーザ user_e のエラーメッセージが変更されました.

django2022-00361

目次に戻る