DjangoのMetaクラス

# Metaクラスの役割
- 使い方は、２パターン
    - モデルクラス内に定義するMetaクラス
    - フォームクラス内に定義するMetaクラス


# モデルクラス内のMeta
- 簡単
- models.モデル内で定義するMetaクラスは、そのモデルに対して、フィールドを並び替えたり、管理画面で表示されるモデル名を変更したり、各モデルに共通するフィールドをまとめておく（このモデルはDBに反映されない）などといった、そのモデルの関するオプションを設定することができる
https://note.com/saito_pythonista/n/n2d02442ea1bf#jPuMt

# フォームクラス内のMeta
- 調査に時間がかかった
- ModelFormやUserCreationForm（ユーザ登録フォーム）を実装するときに使用する
- ポイントは、FormとModelFormの違いを理解すること
- model, fieldsの意味
- [ModelForm](https://docs.djangoproject.com/ja/4.0/topics/forms/modelforms/), UserCreationFormを定義するときに使用する

## FormとModelFormの違い
- 共通部分
    - フォームクラス内に、独自にフィールドを定義すること
        - この場合、独自に定義したフィールドからの入力値はモデルに保存されない
- Form
    - 入力値のバリデーションを行うのみ
        - フィールドからの入力値は、モデルに保存されない
- ModelForm
    - 入力値のバリデーション＋モデルと連携（指定したモデルの全フィールドを自動で呼び出したり、そのフィールドを通して入力値をモデルに保存するかどうかの設定もできる）
    - Metaクラスを用いてモデルのフィールドを自動で共有することができる（＝モデルと連携）
        - model変数：連携するモデルを指定することで、そのモデルの全フィールドをフォームクラス内に自動で定義する（自動生成）
        - fields変数：連携したモデルのどのフィールドをテンプレート上で表示するか。つまり、どのフィールドがユーザからの入力を受け付けるのかを指定する。
        - ここで指定したフィールドは、テンプレート上に表示され、入力を受け付けるため、入力値はmodelで指定したモデルに保存される

## 具体例（フォームクラス内のMeta）
ユーザ登録フォームを作成するためのフォームクラス、[UserCreationFormクラス](https://github.com/django/django/blob/main/django/contrib/auth/forms.py)を使用する場合の具体例を紹介する。
- 独自に定義したフィールド（UserCreationFormで定義されているフィールド）
    - password1, password2
- モデルから自動定義したフィールド（実際にフォームに表示するもの）
    - username, email
- 独自に定義したフィールドとモデルから自動定義したフィールドは、共にフォームをインスタンス化するタイミングで、Context内のformキーの値として格納される（テンプレート上でフォームを表示する具体例については、以下で紹介）

※Djangoは、デフォルトでユーザを保存するためのUserモデルを用意してあるが、独自に作ることを推奨している。そのため、ここでは、AbstractUserクラスを継承した独自のカスタムユーザモデルを定義し、model変数に指定している。
（modelで指定しているCustomuserモデルについては以下で紹介している）

```python: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にフォームで独自に定義したフィールドを指定した場合でもエラーなくテンプレート上で表示されてしまう**

```python:forms.py
# ダメな例
class Meta:
        model = User
        fields = ('username', 'email', 'is_staff', 'password1', 'password2')
```
### カスタムユーザモデル
- デフォルトのUserモデルは、[AbstractUserクラス](https://github.com/django/django/blob/main/django/contrib/auth/models.py)を継承して定義されているため、カスタムユーザモデルを定義する時も使用する
- AbstractUserクラスには、さまざまなフィールドが定義されている

```python: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オブジェクト内に格納されているため、そのまま表示することが可能。

- 自動で定義したフィールドから先に表示される

```html: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 %}
```