Metaクラスの役割
- 使い方は、2パターン
- モデルクラス内に定義するMetaクラス
- フォームクラス内に定義するMetaクラス
モデルクラス内のMeta
- 簡単
- models.モデル内で定義するMetaクラスは、そのモデルに対して、フィールドを並び替えたり、管理画面で表示されるモデル名を変更したり、各モデルに共通するフィールドをまとめておく(このモデルはDBに反映されない)などといった、そのモデルの関するオプションを設定することができる
https://note.com/saito_pythonista/n/n2d02442ea1bf#jPuMt
フォームクラス内のMeta
- 調査に時間がかかった
- ModelFormやUserCreationForm(ユーザ登録フォーム)を実装するときに使用する
- ポイントは、FormとModelFormの違いを理解すること
- model, fieldsの意味
- ModelForm, UserCreationFormを定義するときに使用する
FormとModelFormの違い
- 共通部分
- フォームクラス内に、独自にフィールドを定義すること
- この場合、独自に定義したフィールドからの入力値はモデルに保存されない
- フォームクラス内に、独自にフィールドを定義すること
- Form
- 入力値のバリデーションを行うのみ
- フィールドからの入力値は、モデルに保存されない
- 入力値のバリデーションを行うのみ
- ModelForm
- 入力値のバリデーション+モデルと連携(指定したモデルの全フィールドを自動で呼び出したり、そのフィールドを通して入力値をモデルに保存するかどうかの設定もできる)
- Metaクラスを用いてモデルのフィールドを自動で共有することができる(=モデルと連携)
- model変数:連携するモデルを指定することで、そのモデルの全フィールドをフォームクラス内に自動で定義する(自動生成)
- fields変数:連携したモデルのどのフィールドをテンプレート上で表示するか。つまり、どのフィールドがユーザからの入力を受け付けるのかを指定する。
- ここで指定したフィールドは、テンプレート上に表示され、入力を受け付けるため、入力値はmodelで指定したモデルに保存される
具体例(フォームクラス内のMeta)
ユーザ登録フォームを作成するためのフォームクラス、UserCreationFormクラスを使用する場合の具体例を紹介する。
- 独自に定義したフィールド(UserCreationFormで定義されているフィールド)
- password1, password2
- モデルから自動定義したフィールド(実際にフォームに表示するもの)
- username, email
- 独自に定義したフィールドとモデルから自動定義したフィールドは、共にフォームをインスタンス化するタイミングで、Context内のformキーの値として格納される(テンプレート上でフォームを表示する具体例については、以下で紹介)
※Djangoは、デフォルトでユーザを保存するためのUserモデルを用意してあるが、独自に作ることを推奨している。そのため、ここでは、AbstractUserクラスを継承した独自のカスタムユーザモデルを定義し、model変数に指定している。
(modelで指定しているCustomuserモデルについては以下で紹介している)
forms.py
from django.contrib.auth.forms import UserCreationForm
from .models import CustomUser
class SignUpForm(UserCreationForm):
# UserCreationFormで定義されているフィールド
# password1 = forms.CharField(label=_("Password"), strip=False, widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), help_text=password_validation.password_validators_help_text_html())
# password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}), strip=False, help_text=_("Enter the same password as before, for verification."))
class Meta:
# model:指定したモデルで定義されている全フィールドを自動で定義する(自動生成)
model = CustomUser
# fields:
# modelで指定したモデルの、どのフィールドをテンプレート上に表示するかを指定する。(モデルのフィールド名を指定する)
# つまり、指定したフィールドは、ユーザからの入力を受け付けるため、入力値がモデルに保存される
# フォームクラスで独自に定義したフォームは、ここに書かないこと(混乱の原因)
# モデルに保存する値かどうかを直感的に判断できなくなる
# モデルから自動定義したフィールドも独自に定義したフィールドも{{form}}によってテンプレートに表示できる
fields = ('username', 'email')
混乱するポイント
- Metaクラス内のfields変数に、フォームクラスで独自に定義したフィールドを指定しないこと
- 直感的に、どのフィールドが入力値をモデルに保存するのかがわからなくなるため(modelで指定しているモデルをたどればわかるがめんどくさい)
- fieldsにフォームで独自に定義したフィールドを指定した場合でもエラーなくテンプレート上で表示されてしまう
forms.py
# ダメな例
class Meta:
model = User
fields = ('username', 'email', 'is_staff', 'password1', 'password2')
カスタムユーザモデル
- デフォルトのUserモデルは、AbstractUserクラスを継承して定義されているため、カスタムユーザモデルを定義する時も使用する
- AbstractUserクラスには、さまざまなフィールドが定義されている
models.py
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
pass
# AbstractUserで定義されているフィールド
# password, last_login
# is_superuser
# username, first_name, last_name, email, is_staff, is_active, date_joined, groups, user_permissions
テンプレートでフォームを表示
独自に定義したフィールドもMetaクラスを用いて、モデルから自動で定義したフィールドも共に、formオブジェクト内に格納されているため、そのまま表示することが可能。
- 自動で定義したフィールドから先に表示される
templates/registration/signup.html
{% extends 'base.html' %}
{% block content %}
<h2>ユーザ登録</h2>
<form method='post'>
{% csrf_token %}
{{form.as_p}}
<button type='submit'>ユーザ登録</button>
</form>
{% endblock %}