Djangoでの認証のカスタマイズ
Djangoに付属している認証は、ほとんどの一般的なケースには十分ですが、すぐに使用できるデフォルトでは満たす必要がない場合があります。 プロジェクトで認証をカスタマイズするには、提供されたシステムのどのポイントが拡張可能または交換可能であるかを理解する必要があります。 このドキュメントでは、認証システムをカスタマイズする方法について詳しく説明します。
認証バックエンドは、ユーザーモデルに保存されているユーザー名とパスワードをDjangoのデフォルトとは異なるサービスに対して認証する必要がある場合に拡張可能なシステムを提供します。
モデルにカスタム権限を付与して、Djangoの認証システムで確認できます。
デフォルトのUser
モデルを拡張することも、完全にカスタマイズされたモデルを置換することもできます。
その他の認証ソース
別の認証ソース、つまり、ユーザー名とパスワードまたは認証方法の別のソースにフックする必要がある場合があります。
たとえば、会社には、すべての従業員のユーザー名とパスワードを格納するLDAP設定がすでにある場合があります。 ユーザーがLDAPとDjangoベースのアプリケーションで別々のアカウントを持っている場合、ネットワーク管理者とユーザー自身の両方にとって面倒です。
したがって、このような状況を処理するために、Django認証システムでは他の認証ソースをプラグインできます。 Djangoのデフォルトのデータベースベースのスキームをオーバーライドすることも、デフォルトのシステムを他のシステムと組み合わせて使用することもできます。
Djangoに含まれている認証バックエンドについては、認証バックエンドリファレンスを参照してください。
認証バックエンドの指定
舞台裏では、Djangoは認証をチェックする「認証バックエンド」のリストを維持しています。 誰かが django.contrib.auth.authenticate()を呼び出すと、ユーザーのログイン方法で説明されているように、Djangoはすべての認証バックエンドで認証を試みます。 最初の認証方法が失敗した場合、Djangoはすべてのバックエンドが試行されるまで、2番目の認証方法を試行します。
使用する認証バックエンドのリストは、:setting: `AUTHENTICATION_BACKENDS` 設定で指定されます。 これは、認証方法を知っているPythonクラスを指すPythonパス名のリストである必要があります。 これらのクラスは、Pythonパスのどこにあってもかまいません。
デフォルトでは、:setting: `AUTHENTICATION_BACKENDS` は次のように設定されています。
['django.contrib.auth.backends.ModelBackend']
これは、Djangoユーザーデータベースをチェックし、組み込みの権限を照会する基本認証バックエンドです。 レート制限メカニズムを介したブルートフォース攻撃に対する保護は提供されません。 カスタム認証バックエンドに独自のレート制限メカニズムを実装するか、ほとんどのWebサーバーが提供するメカニズムを使用することができます。
:setting: `AUTHENTICATION_BACKENDS` の順序が重要であるため、同じユーザー名とパスワードが複数のバックエンドで有効な場合、Djangoは最初の正の一致で処理を停止します。
バックエンドで PermissionDenied 例外が発生した場合、認証はすぐに失敗します。 Djangoは後続のバックエンドをチェックしません。
ノート
ユーザーが認証されると、Djangoはユーザーのセッションでユーザーの認証に使用されたバックエンドを保存し、現在認証されているユーザーへのアクセスが必要な場合はいつでも、そのセッションの間同じバックエンドを再利用します。 これは事実上、認証ソースがセッションごとにキャッシュされることを意味します。したがって、:setting: `AUTHENTICATION_BACKENDS` を変更した場合、ユーザーに再試行を強制する必要がある場合は、セッションデータをクリアする必要があります。さまざまな方法を使用して認証します。 これを行う簡単な方法は、Session.objects.all().delete()
を実行することです。
認証バックエンドの作成
認証バックエンドは、get_user(user_id)
とauthenticate(request, **credentials)
の2つの必須メソッドと、承認メソッドに関連するオプションの権限のセットを実装するクラスです。
get_user
メソッドは、user_id
(ユーザー名、データベースIDなどですが、ユーザーオブジェクトの主キーである必要があります)を受け取り、ユーザーオブジェクトまたは [を返します。 X178X]。
authenticate
メソッドは、request
引数と資格情報をキーワード引数として受け取ります。 ほとんどの場合、次のようになります。
from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, username=None, password=None):
# Check the username/password and return a user.
...
ただし、次のようにトークンを認証することもできます。
from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, token=None):
# Check the token and return a user.
...
いずれにせよ、authenticate()
は取得した資格情報を確認し、資格情報が有効な場合はそれらの資格情報に一致するユーザーオブジェクトを返す必要があります。 無効な場合は、None
を返す必要があります。
request
は HttpRequest であり、 authenticate()(バックエンドに渡される)に提供されなかった場合はNone
になる可能性があります。
Django管理者は、Django ユーザーオブジェクトと緊密に結合されています。 これに対処する最善の方法は、バックエンドに存在するユーザーごとにDjango User
オブジェクトを作成することです(たとえば、LDAPディレクトリ、外部SQLデータベースなど)。事前にこれを行うか、authenticate
メソッドでユーザーが最初にログインしたときにこれを行うことができます。
これは、settings.py
ファイルで定義されたユーザー名とパスワードの変数に対して認証し、ユーザーが最初に認証したときにDjango User
オブジェクトを作成するバックエンドの例です。
from django.conf import settings
from django.contrib.auth.backends import BaseBackend
from django.contrib.auth.hashers import check_password
from django.contrib.auth.models import User
class SettingsBackend(BaseBackend):
"""
Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD.
Use the login name and a hash of the password. For example:
ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
"""
def authenticate(self, request, username=None, password=None):
login_valid = (settings.ADMIN_LOGIN == username)
pwd_valid = check_password(password, settings.ADMIN_PASSWORD)
if login_valid and pwd_valid:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
# Create a new user. There's no need to set a password
# because only the password from settings.py is checked.
user = User(username=username)
user.is_staff = True
user.is_superuser = True
user.save()
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
カスタム権限
特定のモデルオブジェクトのカスタム権限を作成するには、permissions
モデルメタ属性を使用します。
この例のTask
モデルは、2つのカスタム権限を作成します。つまり、アプリケーションに固有のTask
インスタンスでユーザーが実行できるアクションと実行できないアクションです。
class Task(models.Model):
...
class Meta:
permissions = [
("change_task_status", "Can change the status of tasks"),
("close_task", "Can remove a task by setting its status as closed"),
]
これが行う唯一のことは、実行時にそれらの追加のアクセス許可を作成することです :djadmin: `manage.py移行 ` (権限を作成する関数はに接続されています post_migrate 信号)。 ユーザーがアプリケーションによって提供される機能にアクセスしようとしているとき(タスクのステータスの変更またはタスクの終了)、コードはこれらのアクセス許可の値のチェックを担当します。上記の例を続けて、以下はユーザーがタスクを閉じることができるかどうかをチェックします。 :
user.has_perm('app.close_task')
既存のUserモデルの拡張
独自のモデルを置き換えることなく、デフォルトの User モデルを拡張する方法は2つあります。 必要な変更が純粋に動作であり、データベースに保存されているものを変更する必要がない場合は、ユーザーに基づいてプロキシモデルを作成できます。 これにより、デフォルトの順序付け、カスタムマネージャー、カスタムモデルメソッドなど、プロキシモデルによって提供されるすべての機能が可能になります。
User
に関連する情報を保存する場合は、 OneToOneField を使用して、追加情報のフィールドを含むモデルを作成できます。 この1対1モデルは、サイトユーザーに関する認証に関連しない情報を格納する可能性があるため、プロファイルモデルと呼ばれることがよくあります。 たとえば、次のような従業員モデルを作成できます。
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)
ユーザーモデルと従業員モデルの両方を持っている既存の従業員FredSmithを想定すると、Djangoの標準の関連モデル規則を使用して関連情報にアクセスできます。
>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
管理者のユーザーページにプロファイルモデルのフィールドを追加するには、アプリのadmin.py
で InlineModelAdmin (この例では StackedInline を使用します)を定義します。 User クラスに登録されているUserAdmin
クラスに追加します。
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import Employee
# Define an inline admin descriptor for Employee model
# which acts a bit like a singleton
class EmployeeInline(admin.StackedInline):
model = Employee
can_delete = False
verbose_name_plural = 'employee'
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = (EmployeeInline,)
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
これらのプロファイルモデルは決して特別なものではありません。ユーザーモデルと1対1のリンクを持っているDjangoモデルにすぎません。 そのため、ユーザーの作成時に自動作成されることはありませんが、 django.db.models.signals.post_save を使用して、必要に応じて関連モデルを作成または更新できます。
関連モデルを使用すると、関連データを取得するための追加のクエリまたは結合が発生します。 必要に応じて、関連フィールドを含むカスタムユーザーモデルの方が適している場合がありますが、プロジェクトのアプリ内のデフォルトのユーザーモデルとの既存の関係により、データベースの追加負荷が正当化される場合があります。
カスタムUserモデルの置き換え
一部の種類のプロジェクトには、Djangoの組み込み User モデルが常に適切であるとは限らない認証要件がある場合があります。 たとえば、一部のサイトでは、ユーザー名の代わりに電子メールアドレスを識別トークンとして使用する方が理にかなっています。
Djangoでは、カスタムモデルを参照する:setting: `AUTH_USER_MODEL` 設定の値を指定することで、デフォルトのユーザーモデルをオーバーライドできます。
AUTH_USER_MODEL = 'myapp.MyUser'
この点線のペアは、Djangoアプリの名前(:setting: `INSTALLED_APPS` に含まれている必要があります)と、ユーザーモデルとして使用するDjangoモデルの名前を示しています。
プロジェクトの開始時にカスタムユーザーモデルを使用する
新しいプロジェクトを開始する場合は、デフォルトのユーザーモデルで十分な場合でも、カスタムユーザーモデルを設定することを強くお勧めします。 このモデルはデフォルトのユーザーモデルと同じように動作しますが、必要に応じて将来カスタマイズすることができます。
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
:setting: `AUTH_USER_MODEL` を指すことを忘れないでください。 移行を作成する前、またはmanage.py migrate
を初めて実行する前に、これを実行してください。
また、アプリのadmin.py
にモデルを登録します。
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
admin.site.register(User, UserAdmin)
プロジェクトの途中でカスタムユーザーモデルに変更する
データベーステーブルの作成後に:setting: `AUTH_USER_MODEL` を変更すると、外部キーや多対多の関係などに影響するため、非常に困難になります。
この変更は自動的に行うことはできず、スキーマを手動で修正し、古いユーザーテーブルからデータを移動し、場合によっては一部の移行を手動で再適用する必要があります。 手順の概要については、:ticket: `25313` を参照してください。
スワップ可能なモデルに対するDjangoの動的依存関係機能の制限により、:setting: `AUTH_USER_MODEL` で参照されるモデルは、アプリの最初の移行時に作成する必要があります(通常は0001_initial
と呼ばれます)。 そうしないと、依存関係の問題が発生します。
さらに、動的な依存関係のためにDjangoが依存関係ループを自動的に解除できないため、移行の実行時にCircularDependencyError
が発生する可能性があります。 このエラーが表示された場合は、ユーザーモデルに依存するモデルを2番目の移行に移動して、ループを解除する必要があります。 (ForeignKey
を相互に持つ2つの正規モデルを作成し、通常の方法を確認したい場合は、makemigrations
がその循環依存関係をどのように解決するかを確認できます。)
再利用可能なアプリとAUTH_USER_MODEL
再利用可能なアプリは、カスタムユーザーモデルを実装するべきではありません。 プロジェクトは多くのアプリを使用する可能性があり、カスタムユーザーモデルを実装した2つの再利用可能なアプリを一緒に使用することはできませんでした。 アプリにユーザーごとの情報を保存する必要がある場合は、以下の説明に従って、 ForeignKey または OneToOneField からsettings.AUTH_USER_MODEL
を使用します。
Userモデルを参照する
User を直接参照する場合(たとえば、外部キーで参照する場合)、:setting: `AUTH_USER_MODEL` 設定が変更されたプロジェクトではコードが機能しません。別のユーザーモデルに。
- get_user_model()
User を直接参照する代わりに、
django.contrib.auth.get_user_model()
を使用してユーザーモデルを参照する必要があります。 このメソッドは、現在アクティブなユーザーモデルを返します。指定されている場合はカスタムユーザーモデル、それ以外の場合は User を返します。ユーザーモデルに対して外部キーまたは多対多の関係を定義する場合は、:setting: `AUTH_USER_MODEL` 設定を使用してカスタムモデルを指定する必要があります。 例えば:
from django.conf import settings from django.db import models class Article(models.Model): author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, )
ユーザーモデルから送信される信号に接続する場合は、:setting: `AUTH_USER_MODEL` 設定でカスタムモデルを指定する必要があります。 例えば:
from django.conf import settings from django.db.models.signals import post_save def post_save_receiver(sender, instance, created, **kwargs): pass post_save.connect(post_save_receiver, sender=settings.AUTH_USER_MODEL)
一般的に、インポート時に実行されるコードで:setting: `AUTH_USER_MODEL` 設定を使用してユーザーモデルを参照するのが最も簡単ですが、Djangoが実行されている間に
get_user_model()
を呼び出すこともできます。モデルをインポートするので、models.ForeignKey(get_user_model(), ...)
を使用できます。アプリが
@override_settings(AUTH_USER_MODEL=...)
などを使用して複数のユーザーモデルでテストされ、get_user_model()
の結果をモジュールレベルの変数にキャッシュする場合、 setting_changedをリッスンする必要がある場合があります。 キャッシュをクリアする信号。 例えば:from django.apps import apps from django.contrib.auth import get_user_model from django.core.signals import setting_changed from django.dispatch import receiver @receiver(setting_changed) def user_model_swapped(**kwargs): if kwargs['setting'] == 'AUTH_USER_MODEL': apps.clear_cache() from myapp import some_module some_module.UserModel = get_user_model()
カスタムユーザーモデルの指定
カスタムユーザーモデルを使用してプロジェクトを開始するときは、これがプロジェクトにとって正しい選択であるかどうかを検討するのをやめてください。
すべてのユーザー関連情報を1つのモデルに保持することで、関連モデルを取得するための追加またはより複雑なデータベースクエリが不要になります。 一方、カスタムユーザーモデルと関係のあるモデルにアプリ固有のユーザー情報を保存する方が適切な場合があります。 これにより、各アプリは、他のアプリによる想定と矛盾したり破られたりすることなく、独自のユーザーデータ要件を指定できます。 また、ユーザーモデルを可能な限りシンプルに保ち、認証に重点を置き、Djangoがカスタムユーザーモデルが満たすと期待する最小要件に従うことも意味します。
デフォルトの認証バックエンドを使用する場合、モデルには、識別目的で使用できる単一の一意のフィールドが必要です。 これは、ユーザー名、電子メールアドレス、またはその他の一意の属性にすることができます。 一意でないユーザー名フィールドは、それをサポートできるカスタム認証バックエンドを使用する場合に許可されます。
準拠したカスタムユーザーモデルを構築する最も簡単な方法は、 AbstractBaseUser から継承することです。 AbstractBaseUser は、ハッシュ化されたパスワードやトークン化されたパスワードのリセットなど、ユーザーモデルのコア実装を提供します。 次に、いくつかの重要な実装の詳細を提供する必要があります。
- class models.CustomUser
- USERNAME_FIELD
一意の識別子として使用されるユーザーモデルのフィールドの名前を説明する文字列。 これは通常、ある種のユーザー名になりますが、電子メールアドレスやその他の一意の識別子にすることもできます。 一意でないユーザー名をサポートできるカスタム認証バックエンドを使用しない限り、フィールドは一意である必要があります(つまり、
unique=True
が定義に設定されている)。次の例では、フィールド
identifier
が識別フィールドとして使用されています。class MyUser(AbstractBaseUser): identifier = models.CharField(max_length=40, unique=True) ... USERNAME_FIELD = 'identifier'
- EMAIL_FIELD
User
モデルの電子メールフィールドの名前を説明する文字列。 この値は、 get_email_field_name()によって返されます。
- REQUIRED_FIELDS
:djadmin: `createsuperuser` 管理コマンドを使用してユーザーを作成するときにプロンプトが表示されるフィールド名のリスト。 ユーザーは、これらの各フィールドに値を入力するように求められます。 空白が
False
または未定義のフィールドを含める必要があり、ユーザーがインタラクティブに作成されたときにプロンプトが表示される追加のフィールドを含めることができます。REQUIRED_FIELDS
は、管理者でユーザーを作成するなど、Djangoの他の部分では効果がありません。たとえば、生年月日と身長の2つの必須フィールドを定義するユーザーモデルの部分的な定義は次のとおりです。
class MyUser(AbstractBaseUser): ... date_of_birth = models.DateField() height = models.FloatField() ... REQUIRED_FIELDS = ['date_of_birth', 'height']
ノート
REQUIRED_FIELDS
には、ユーザーモデルのすべての必須フィールドが含まれている必要がありますが、にはUSERNAME_FIELD
またはpassword
が含まれていない必要があります。これらのフィールドは、常にプロンプトが表示されるためです。
- is_active
ユーザーが「アクティブ」と見なされるかどうかを示すブール属性。 この属性は、デフォルトで
True
に設定されているAbstractBaseUser
の属性として提供されます。 どのように実装するかは、選択した認証バックエンドの詳細によって異なります。 詳細については、組み込みユーザーモデルの is_active属性のドキュメントを参照してください。
- get_full_name()
オプション。 フルネームなど、ユーザーのより長い正式な識別子。 実装されている場合、これは django.contrib.admin のオブジェクトの履歴にユーザー名と一緒に表示されます。
- get_short_name()
オプション。 名など、ユーザーの短い非公式の識別子。 実装されている場合、これは django.contrib.admin のヘッダーにあるユーザーへの挨拶のユーザー名を置き換えます。
AbstractBaseUser
をインポートしていますAbstractBaseUser
とBaseUserManager
はdjango.contrib.auth.base_user
からインポートできるため、:setting: `INSTALLED_APPS` にdjango.contrib.auth
を含めずにインポートできます。
次の属性とメソッドは、 AbstractBaseUser のすべてのサブクラスで使用できます。
- class models.AbstractBaseUser
- get_username()
USERNAME_FIELD
で指定されたフィールドの値を返します。
- clean()
normalize_username()を呼び出して、ユーザー名を正規化します。 このメソッドをオーバーライドする場合は、必ず
super()
を呼び出して正規化を保持してください。
- classmethod get_email_field_name()
EMAIL_FIELD 属性で指定された電子メールフィールドの名前を返します。
EMAIL_FIELD
が指定されていない場合、デフォルトは'email'
です。
- classmethod normalize_username(username)
NFKC Unicode正規化をユーザー名に適用して、異なるUnicodeコードポイントを持つ視覚的に同一の文字が同一であると見なされるようにします。
- is_authenticated
常に
True
である読み取り専用属性(常にFalse
であるAnonymousUser.is_authenticated
とは対照的)。 これは、ユーザーが認証されているかどうかを確認する方法です。 これは、権限を意味するものではなく、ユーザーがアクティブであるか、有効なセッションを持っているかどうかを確認しません。 通常、request.user
でこの属性をチェックして、 AuthenticationMiddleware (現在ログインしているユーザーを表す)によって入力されているかどうかを確認しますが、この属性がTrue
任意のユーザーインスタンス。
- is_anonymous
常に
False
である読み取り専用属性。 これは、 User オブジェクトと AnonymousUser オブジェクトを区別する方法です。 通常、この属性よりも is_authenticated を使用することをお勧めします。
- set_password(raw_password)
パスワードのハッシュを処理しながら、ユーザーのパスワードを指定された生の文字列に設定します。 AbstractBaseUser オブジェクトを保存しません。
raw_passwordが
None
の場合、 set_unusable_password()が使用されたかのように、パスワードは使用できないパスワードに設定されます。
- check_password(raw_password)
指定された生の文字列がユーザーの正しいパスワードである場合、
True
を返します。 (これにより、比較を行う際のパスワードハッシュが処理されます。)
- set_unusable_password()
パスワードが設定されていないことをユーザーにマークします。 これは、パスワードに空白の文字列を使用することと同じではありません。 このユーザーの check_password()は、
True
を返すことはありません。 AbstractBaseUser オブジェクトを保存しません。アプリケーションの認証がLDAPディレクトリなどの既存の外部ソースに対して行われる場合は、これが必要になることがあります。
- has_usable_password()
このユーザーに対して set_unusable_password()が呼び出された場合、
False
を返します。
- get_session_auth_hash()
パスワードフィールドのHMACを返します。 パスワード変更時のセッション無効化に使用されます。
バージョン3.1で変更:ハッシュアルゴリズムがSHA-256に変更されました。
AbstractUser サブクラス AbstractBaseUser :
- class models.AbstractUser
- ;; clean()
- BaseUserManager.normalize_email()を呼び出して、電子メールを正規化します。 このメソッドをオーバーライドする場合は、必ず
super()
を呼び出して正規化を保持してください。
カスタムユーザーモデルのマネージャーを作成する
また、ユーザーモデルのカスタムマネージャーを定義する必要があります。 ユーザーモデルでusername
、email
、is_staff
、is_active
、is_superuser
、last_login
、[X97X ] フィールドはDjangoのデフォルトユーザーと同じで、Djangoの UserManager をインストールできます。 ただし、ユーザーモデルで異なるフィールドを定義する場合は、 BaseUserManager を拡張して2つの追加メソッドを提供するカスタムマネージャーを定義する必要があります。
- class models.CustomUserManager
- create_user(username_field, password=None, **other_fields)
create_user()
のプロトタイプは、ユーザー名フィールドに加えて、すべての必須フィールドを引数として受け入れる必要があります。 たとえば、ユーザーモデルがユーザー名フィールドとしてemail
を使用し、必須フィールドとしてdate_of_birth
がある場合、create_user
は次のように定義する必要があります。def create_user(self, email, date_of_birth, password=None): # create user here ...
- create_superuser(username_field, password=None, **other_fields)
create_superuser()
のプロトタイプは、ユーザー名フィールドに加えて、すべての必須フィールドを引数として受け入れる必要があります。 たとえば、ユーザーモデルがユーザー名フィールドとしてemail
を使用し、必須フィールドとしてdate_of_birth
がある場合、create_superuser
は次のように定義する必要があります。def create_superuser(self, email, date_of_birth, password=None): # create superuser here ...
USERNAME_FIELD または REQUIRED_FIELDS の ForeignKey の場合、これらのメソッドは to_field ( primary_key byデフォルト)既存のインスタンスの。
BaseUserManager は、次のユーティリティメソッドを提供します。
- class models.BaseUserManager
- classmethod normalize_email(email)
電子メールアドレスのドメイン部分を小文字にすることにより、電子メールアドレスを正規化します。
- get_by_natural_key(username)
USERNAME_FIELD
で指定されたフィールドの内容を使用してユーザーインスタンスを取得します。
- make_random_password(length=10, allowed_chars='abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789')
指定された長さと許可された文字列のランダムなパスワードを返します。
allowed_chars
のデフォルト値には、次のようなユーザーの混乱を引き起こす可能性のある文字が含まれていないことに注意してください。i
、l
、I
、および1
(小文字i、小文字L、大文字i、および番号1)o
、O
、および0
(小文字のo、大文字のo、およびゼロ)
Djangoのデフォルトの拡張User
Djangoの User モデルに完全に満足しているが、プロファイル情報を追加したい場合は、 django.contrib.auth.models.AbstractUser をサブクラス化してカスタムプロファイルを追加できます。 カスタムユーザーモデルの指定の「モデル設計の考慮事項」の注記で説明されているように、別のモデルをお勧めします。 AbstractUser
は、デフォルトの User の完全な実装を抽象モデルとして提供します。
カスタムユーザーと組み込みの認証フォーム
Djangoの組み込みのフォームおよびビューは、使用しているユーザーモデルについて特定の仮定を行っています。
次のフォームは、 AbstractBaseUser のすべてのサブクラスと互換性があります。
- AuthenticationForm : USERNAME_FIELD で指定されたユーザー名フィールドを使用します。
SetPasswordForm
PasswordChangeForm
AdminPasswordChangeForm
次のフォームは、ユーザーモデルに関する仮定を作成し、それらの仮定が満たされている場合はそのまま使用できます。
- PasswordResetForm :ユーザーモデルに、 get_email_field_name()(デフォルトでは
email
)によって返される名前のユーザーの電子メールアドレスを格納するフィールドがあると想定します。ユーザーとis_active
という名前のブールフィールドを識別して、非アクティブなユーザーのパスワードがリセットされないようにします。
最後に、次のフォームは User に関連付けられており、カスタムユーザーモデルで機能するように書き直すか拡張する必要があります。
カスタムユーザーモデルがAbstractUser
のサブクラスである場合、次の方法でこれらのフォームを拡張できます。
from django.contrib.auth.forms import UserCreationForm
from myapp.models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = CustomUser
fields = UserCreationForm.Meta.fields + ('custom_field',)
カスタムユーザーと django.contrib.admin
カスタムユーザーモデルを管理者とも連携させる場合は、ユーザーモデルでいくつかの追加の属性とメソッドを定義する必要があります。 これらのメソッドにより、管理者は管理コンテンツへのユーザーのアクセスを制御できます。
- class models.CustomUser
- is_staff
- ユーザーが管理サイトへのアクセスを許可されている場合は、
True
を返します。
- is_active
- ユーザーアカウントが現在アクティブな場合は、
True
を返します。
- has_perm(perm, obj=None):
- ユーザーが指定された権限を持っている場合、
True
を返します。obj
が指定されている場合は、特定のオブジェクトインスタンスに対して権限を確認する必要があります。
- has_module_perms(app_label):
- ユーザーが指定されたアプリのモデルにアクセスする権限を持っている場合、
True
を返します。
また、カスタムユーザーモデルを管理者に登録する必要があります。 カスタムユーザーモデルがdjango.contrib.auth.models.AbstractUser
を拡張する場合は、Djangoの既存のdjango.contrib.auth.admin.UserAdmin
クラスを使用できます。 ただし、ユーザーモデルが AbstractBaseUser を拡張する場合は、カスタムModelAdmin
クラスを定義する必要があります。 デフォルトのdjango.contrib.auth.admin.UserAdmin
をサブクラス化できる場合があります。 ただし、カスタムユーザークラスにないdjango.contrib.auth.models.AbstractUser
のフィールドを参照する定義をオーバーライドする必要があります。
ノート
django.contrib.auth.admin.UserAdmin
のサブクラスであるカスタムModelAdmin
を使用している場合は、カスタムフィールドをfieldsets
(ユーザーの編集に使用するフィールド用)に追加する必要があります。 〜add_fieldsets
(ユーザー作成時に使用するフィールド用)。 例えば:
from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
...
fieldsets = UserAdmin.fieldsets + (
(None, {'fields': ('custom_field',)}),
)
add_fieldsets = UserAdmin.add_fieldsets + (
(None, {'fields': ('custom_field',)}),
)
詳細については、完全な例を参照してください。
カスタムユーザーと権限
Djangoのパーミッションフレームワークを独自のユーザークラスに簡単に含めることができるように、Djangoは PermissionsMixin を提供しています。 これは、ユーザーモデルのクラス階層に含めることができる抽象モデルであり、Djangoのアクセス許可モデルをサポートするために必要なすべてのメソッドとデータベースフィールドを提供します。
PermissionsMixin は、次のメソッドと属性を提供します。
- class models.PermissionsMixin
- is_superuser
ブール値。 このユーザーが明示的に割り当てずにすべての権限を持っていることを指定します。
- get_user_permissions(obj=None)
ユーザーが直接持っている一連のアクセス許可文字列を返します。
obj
が渡された場合、この特定のオブジェクトのユーザー権限のみを返します。
- get_group_permissions(obj=None)
グループを通じて、ユーザーが持っている一連のアクセス許可文字列を返します。
obj
が渡された場合、この特定のオブジェクトのグループ権限のみを返します。
- get_all_permissions(obj=None)
グループ権限とユーザー権限の両方を通じて、ユーザーが持っている権限文字列のセットを返します。
obj
が渡された場合、この特定のオブジェクトのアクセス許可のみが返されます。
- has_perm(perm, obj=None)
ユーザーが指定された権限を持っている場合、
True
を返します。ここで、perm
は"<app label>.<permission codename>"
の形式です(権限を参照)。 User.is_active と is_superuser が両方ともTrue
の場合、このメソッドは常にTrue
を返します。obj
が渡された場合、このメソッドはモデルのアクセス許可をチェックしませんが、この特定のオブジェクトのアクセス許可をチェックします。
- has_perms(perm_list, obj=None)
ユーザーが指定された各権限を持っている場合、
True
を返します。各権限は、"<app label>.<permission codename>"
の形式です。 User.is_active と is_superuser が両方ともTrue
の場合、このメソッドは常にTrue
を返します。obj
が渡された場合、このメソッドはモデルの権限ではなく、特定のオブジェクトの権限を確認します。
- has_module_perms(package_name)
ユーザーが指定されたパッケージ(Djangoアプリラベル)で何らかの権限を持っている場合、
True
を返します。 User.is_active と is_superuser が両方ともTrue
の場合、このメソッドは常にTrue
を返します。
カスタムユーザーとプロキシモデル
カスタムユーザーモデルの制限の1つは、カスタムユーザーモデルをインストールすると、ユーザーを拡張するプロキシモデルが破損することです。 プロキシモデルは、具体的な基本クラスに基づいている必要があります。 カスタムユーザーモデルを定義することで、基本クラスを確実に識別するDjangoの機能を削除します。
プロジェクトでプロキシモデルを使用する場合は、プロキシを変更してプロジェクトで使用されているユーザーモデルを拡張するか、プロキシの動作を User サブクラスにマージする必要があります。
完全な例
これは、管理者準拠のカスタムユーザーアプリの例です。 このユーザーモデルは、ユーザー名として電子メールアドレスを使用し、必要な生年月日があります。 ユーザーアカウントのadmin
フラグ以外の権限チェックは提供されません。 このモデルは、ユーザー作成フォームを除く、すべての組み込みの認証フォームおよびビューと互換性があります。 この例は、ほとんどのコンポーネントがどのように連携するかを示していますが、本番環境で使用するためにプロジェクトに直接コピーすることを意図したものではありません。
このコードはすべて、カスタム認証アプリのmodels.py
ファイルに存在します。
from django.db import models
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
class MyUserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
date_of_birth=date_of_birth,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, date_of_birth, password=None):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
user = self.create_user(
email,
password=password,
date_of_birth=date_of_birth,
)
user.is_admin = True
user.save(using=self._db)
return user
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
date_of_birth = models.DateField()
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['date_of_birth']
def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
@property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
次に、このカスタムユーザーモデルをDjangoの管理者に登録するには、アプリのadmin.py
ファイルに次のコードが必要です。
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.core.exceptions import ValidationError
from customauth.models import MyUser
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)
class Meta:
model = MyUser
fields = ('email', 'date_of_birth')
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super().save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
class UserChangeForm(forms.ModelForm):
"""A form for updating users. Includes all the fields on
the user, but replaces the password field with admin's
disabled password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = MyUser
fields = ('email', 'password', 'date_of_birth', 'is_active', 'is_admin')
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserChangeForm
add_form = UserCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'date_of_birth', 'is_admin')
list_filter = ('is_admin',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ('date_of_birth',)}),
('Permissions', {'fields': ('is_admin',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'date_of_birth', 'password1', 'password2'),
}),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
# Now register the new UserAdmin...
admin.site.register(MyUser, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)
最後に、settings.py
の:setting: `AUTH_USER_MODEL` 設定を使用して、プロジェクトのデフォルトのユーザーモデルとしてカスタムモデルを指定します。
AUTH_USER_MODEL = 'customauth.MyUser'
バージョン3.2で変更:古いバージョンでは、ReadOnlyPasswordHashField
はデフォルトで無効ではなく、UserChangeForm.clean_password()
はユーザーに関係なく初期値を返す必要があります提供します。