DjangoのCreateViewについて理解する

## 初めに
Django の汎用ビューである```CreateView```では、主に新しいオブジェクト（通常はデータベースモデルのインスタンス）を簡単に作成し、保存することができます。

全体コードは[こちら](https://github.com/django/django/blob/main/django/views/generic/edit.py)

## CreateViewクラスの継承クラス
CreateViewクラスでは、```SingleObjectTemplateResponseMixin```クラスと```BaseCreateView```クラスを継承しており、HTMLテンプレートを使ったレスポンスのレンダリングと新しいオブジェクトの作成機能を統合しています。

```python:CreateView
class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
    template_name_suffix = "_form"
```

### SingleObjectTemplateResponseMixinクラス
このMixinクラスは、```TemplateResponseMixin```クラスを継承しており、特定のオブジェクトに関連するHTMLテンプレートを使用してレスポンスをレンダリングする機能を提供します。

### BaseCreateViewクラス
このクラスは、```ProcessFormView```と```ModelFormMixin```を継承し、新しいオブジェクトを作成するためのコア機能を提供します。


## SingleObjectTemplateResponseMixin
テンプレートレスポンスを生成するためのメソッドを提供します


```python:SingleObjectTemplateResponseMixin
class SingleObjectTemplateResponseMixin(TemplateResponseMixin):
    template_name_field = None
    template_name_suffix = "_detail"

    def get_template_names(self):
        try:
            names = super().get_template_names()
        except ImproperlyConfigured:
            names = []

            if self.object and self.template_name_field:
                name = getattr(self.object, self.template_name_field, None)
                if name:
                    names.insert(0, name)

            if isinstance(self.object, models.Model):
                object_meta = self.object._meta
                names.append(
                    "%s/%s%s.html"
                    % (
                        object_meta.app_label,
                        object_meta.model_name,
                        self.template_name_suffix,
                    )
                )
            elif getattr(self, "model", None) is not None and issubclass(
                self.model, models.Model
            ):
                names.append(
                    "%s/%s%s.html"
                    % (
                        self.model._meta.app_label,
                        self.model._meta.model_name,
                        self.template_name_suffix,
                    )
                )

            if not names:
                raise

        return names
```
### get_template_namesメソッド
親のget_template_namesメソッドをオーバーライドしており、テンプレート名を取得する機能を提供します。

まず親の```get_template_names```メソッドを実行します。
親のtemplate_nameメソッドでは、```template_name```が指定されていない場合エラーになりますが、ここではさらにモデル名やアプリ名を元にテンプレート名を推測します。

親のtemplate_nameメソッドでエラーを検知した場合、まず```self.object```と```self.template_name_field```が存在する場合、これらの値をもとにgetattr関数を使ってテンプレートネームの取得を試みます。

次にself.objectがモデルインスタンスかどうかをチェックし、self.object._metaでモデルインスタンスのメタデータを取得します。
このメタデータをもとにテンプレートネームを生成します。

最後はビューにmodel属性が設定されており、その属性がモデルクラスであるかどうかをチェックします。そうであればビューに関連付けられたモデルの名前を使用してデフォルトのテンプレート名を生成することができます。

CreateViewクラスでは新規にオブジェクトを作成するので、当然self.objectはNoneとなります。
つまり、template_nameを指定しない場合、フォームにモデルが設定されているか確認して、なければエラーになります。

### 継承クラス TemplateResponseMixin
```TemplateResponseMixin```クラスを継承しています。

このクラスではレスポンスを返す```render_to_response```メソッドが実行されますが、ここで使用される```get_template_names```メソッドは先ほどオーバーライドしたメソッドが使用されることになります。

## BaseCreateViewクラス
新しいデータベースオブジェクトの作成にを行います。
このクラスは、HTTPのGETリクエストに対してフォームを表示し（getメソッド）、POSTリクエストによって提出されたフォームデータを処理します（postメソッド）。
```ModelFormMixin```を通じて、使用するフォームクラスや成功時のリダイレクト先URLの設定も行います。

```python
class BaseCreateView(ModelFormMixin, ProcessFormView):

    def get(self, request, *args, **kwargs):
        self.object = None
        return super().get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.object = None
        return super().post(request, *args, **kwargs)
```

### getメソッド
HTTPのGETリクエストを処理し、基本的にフォームが初めて表示される際に呼び出されるメソッドになります。(getでformのページをリクエストし、formに入力した値をもとに作成する場合はpostメソッドでリクエストするためです)

```self.object = None```の行は、このビューが新しいオブジェクトの作成を扱っていることを示しています。
既存のオブジェクトを編集する場合とは異なり、新しいインスタンスを作成するためには、object 属性が None である必要があるためです。

最後に親クラスのgetメソッドを呼び出し、フォームをレンダリングしてレスポンスを返しています。

### postメソッド
HTTPのPOSTリクエストを処理し、ユーザーがフォームにデータを入力して送信した際に実行されます。
後はgetメソッドと同様になります。

## ModelFormMixinとProcessFormViewの相互作用
```ModelFormMixin```は、ビューに関連付けられたモデルからフォームクラスを自動生成する機能を提供します。一方、```ProcessFormView```はフォームの表示と処理の基本フローを担当し、```ModelFormMixin```と連携して、フォームのデータを効果的にハンドリングします。


## BaseCreateViewの継承① ModelFormMixinクラス
モデルに基づいたフォーム（ModelForm）の表示と処理を容易にするためのMixinです。
ビューで使用するModelFormクラスやフォームに渡す値、成功時のURLを決定します。


```python:ModelFormMixin
class ModelFormMixin(FormMixin, SingleObjectMixin):

    fields = None

    def get_form_class(self):
        if self.fields is not None and self.form_class:
            raise ImproperlyConfigured(
                "Specifying both 'fields' and 'form_class' is not permitted."
            )
        if self.form_class:
            return self.form_class
        else:
            if self.model is not None:
                model = self.model
            elif getattr(self, "object", None) is not None:
                model = self.object.__class__
            else:
                model = self.get_queryset().model

            if self.fields is None:
                raise ImproperlyConfigured(
                    "Using ModelFormMixin (base class of %s) without "
                    "the 'fields' attribute is prohibited." % self.__class__.__name__
                )

            return model_forms.modelform_factory(model, fields=self.fields)

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        if hasattr(self, "object"):
            kwargs.update({"instance": self.object})
        return kwargs

    def get_success_url(self):
        if self.success_url:
            url = self.success_url.format(**self.object.__dict__)
        else:
            try:
                url = self.object.get_absolute_url()
            except AttributeError:
                raise ImproperlyConfigured(
                    "No URL to redirect to.  Either provide a url or define"
                    " a get_absolute_url method on the Model."
                )
        return url
```
### get_form_classメソッド
ビューで使用するModelFormクラスを返します。

このメソッドは、```form_class```属性が設定されていればそれを使用し、設定されていなければ、ビューに関連付けられたモデルからフォームクラスを動的に生成します。

もしビューが```fields```と```form_class```の両方を同時に設定している場合、ImproperlyConfigured エラーが発生し、```form_class```のみ設定されていればform_classを返します。

もし```form_class```が未設定の場合は、```self.model```またはビューが現在操作しているオブジェクト（```self.object```）、それもない場合は```get_queryset```メソッドを呼び出してクエリセットを取得し、そのクエリセットからモデルクラスを取得します。
上記で取得した```model```と```fields```属性で```modelform_factory```関数を使用して関連モデルに基づくフォームクラスを自動的に生成します。
なので、```form_class```が未定義で```fields```が指定されていなければエラーとなります。

CreateViewは新規作成であり、self.objectはNoneのため、ここでobjectが定義されることはありません。

### get_form_kwargsメソッド
フォームに表示させるデフォルトの情報の設定と、複数のフォームを区別させることができます。
親(FormMixinクラス)のget_form_kwargsでは、デフォルトの情報の設定を```get_initial```メソッドで、複数のフォーム区別を```get_prefix```メソッドで行っています。

```python:FormMixinクラスの一部
class FormMixin(ContextMixin):

    initial = {}
    form_class = None
    success_url = None
    prefix = None

    def get_initial(self):
        return self.initial.copy()

    def get_prefix(self):
        return self.prefix

    .... 省略

    def get_form_kwargs(self):
        kwargs = {
            "initial": self.get_initial(),
            "prefix": self.get_prefix(),
        }

        if self.request.method in ("POST", "PUT"):
            kwargs.update(
                {
                    "data": self.request.POST,
                    "files": self.request.FILES,
                }
            )
        return kwargs
```
```python:prefixの使用例

#同じfieldを持つ二つのform
class RegistrationForm(forms.Form): #登録フォーム
    username = forms.CharField()
    password = forms.CharField(widget=forms.PasswordInput)

class LoginForm(forms.Form): #ログインフォーム
    username = forms.CharField()
    password = forms.CharField(widget=forms.PasswordInput)


#ビューの定義
class MyView(FormView):
    template_name = 'my_template.html'

    def get_context_data(self, **kwargs):
        context = super(MyView, self).get_context_data(**kwargs)
        context['registration_form'] = RegistrationForm(prefix='register')
        context['login_form'] = LoginForm(prefix='login')
        return context

#テンプレートでの使用
<form method="post">
    {{ registration_form.as_p }}
    <button type="submit">Register</button>
</form>

<form method="post">
    {{ login_form.as_p }}
    <button type="submit">Login</button>
</form>
```
上記prefixの例では、登録フォームのフィールドには```register-username```、```register-password```という名前が付けられ、ログインフォームのフィールドには```login-username```、```login-password```という名前が付けられます。
これにより、同じページ上でusernameという名前のフィールドが衝突することなく、サーバー側で正確にどのフォームのデータかを識別することができます。

```python:initialの使用例
class ProfileUpdateView(FormView):
    template_name = 'profile_update.html'
    form_class = ProfileForm

    def get_initial(self):
        # 初期データを取得するロジック
        initial = super(ProfileUpdateView, self).get_initial()
        initial['username'] = self.request.user.username
        initial['email'] = self.request.user.email
        # その他の初期データを辞書に追加する
        return initial

```
ユーザーがフォームを表示すると、username と email フィールドには既に値が入力されています。
get_initialは通常、GETリクエスト時にフォームを表示する際に使用されます。
POSTリクエストやフォームのバリデーション後には、通常、ユーザーが入力したデータが優先され、get_initial で設定した値は無視されます。


今回だと、、ビューに```object```属性（現在のモデルインスタンス）があれば、このオブジェクトをフォームの```instance```引数として追加します。
つまりobjectが存在すれば、デフォルトの値を指定します。

prefixとinitialの使用例は、フォームのカスタマイズとデータの事前入力の方法を示しています。これらはCreateViewやその他のフォームビューでのフォームの柔軟な管理に役立ちます。


### get_success_urlメソッド
フォームが有効に処理された後にユーザーをリダイレクトするURLを返します。

```success_url```属性にURLを指定することで、urlを返します。
もし```success_url```属性がない場合、self.object（作成または更新されたオブジェクト）の get_absolute_url メソッドを呼び出し、その戻り値をリダイレクトURLとして使用します。
ただ、```self.object```に```get_absolute_url```メソッドが定義されていない場合エラーになります。

### 継承クラス FormMixinクラス
このクラスはフォームの初期化、処理、成功時の動作などを管理します。

```form_valid```メソッドは、フォームが有効なデータ（バリデーションを通過したデータ）を持っている場合に呼び出されます。
通常、このメソッドはフォームのデータを保存し、成功時のURLへリダイレクトする処理を行います。

```form_invalid```メソッド
フォームが無効なデータ（バリデーションエラーを含むデータ）を持っている場合に呼び出されます。
通常、フォームとエラーメッセージを含むレスポンスを再表示する処理を行います。


## BaseCreateViewの継承② ProcessFormViewクラス
主にフォームの表示と処理を担当する基本的なビュークラスです。
HTTPのGETリクエストに対してフォームをレンダリングし、POSTリクエストでフォームのデータを処理するために使用されます。

```python:ProcessFormView
class ProcessFormView(View):
    
    def get(self, request, *args, **kwargs):
        return self.render_to_response(self.get_context_data())

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def put(self, *args, **kwargs):
        return self.post(*args, **kwargs)
```

```View```クラスを継承していますが、このクラスはリクエストメソッドに応じてそれに対応する名前のメソッドを実行しています。
つまりGETリクエストやPOSTリクエストを受け取れば、上記```ProcessFormView```クラスの対応するメソッドが実行されるということです。

Viewの基底クラスについての説明は[こちら](https://qiita.com/ryoutancobu/items/c7f446a1f2e8e901fcee)