【Django】CreateViewの使い方(クラスベースビューでの新規登録ページの実現)

CreateViewでのクラスベースビューの実現方法解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、Django フレームワークで定義される CreateView の使い方について説明していきます。

この CreateViewView というクラスのサブクラスの1つであり、ビューをクラスベースで作成する際に利用するクラスとなります。この View のサブクラス を継承し、さらにクラス変数を定義したりメソッドをオーバーライドすることで、あなたが開発したいアプリに応じたビューを作成することが可能となります。

この辺りのクラスベースビューやクラスベースビューの作り方については下記ページで解説していますので、詳しくはこちらをご参照ください。

クラスベースビューの解説ページアイキャッチ 【Django入門14】クラスベースビューの基本

CreateView

では、まずは CreateView について解説していきたいと思います。

CreateView は、名前の通り、データベースのテーブルへのレコードの新規登録を行うページを実現する View のサブクラス となります。Django において、データベースのテーブルはモデルクラス(モデル)であり、レコードとはモデルクラスのインスタンスとなります。したがって、CreateView は特定のモデルクラスのテーブルにインスタンスを新規登録するためのページを実現する View のサブクラス とも言えます。

この CreateViewdjango.views.generic から import して利用します。

CreateViewのimport
from django.views.generic import CreateView

例えば、ユーザーの新規登録ページ、コメントの新規投稿ページなどなど、ウェブアプリにおいてはデータベースへのレコードの新規登録を行うためのページが必要になるようなことが多いです。このようなページを実現するビューは、CreateView を継承することで簡単に作成することが可能です。

CreateViewで実現可能なビューの例

また、View のサブクラス には、下記ページでも紹介している ListViewDetailView が存在しています。

DjangoのListViewの解説ページアイキャッチ 【Django】ListViewの使い方(クラスベースビューでの一覧リストページの実現) DjangoのDetailViewの解説ページアイキャッチ 【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現)

これらのクラスは表示に特化したものであり、基本的には HTTP のリクエストにおける GET にのみ対応しています。そして、これらのクラスではリクエストを受け取った際に get メソッドが実行されるようになっています。

それに対し、CreateViewGET のみだけではなく POST にも対応しています。そのため、ページの表示だけでなく、ユーザーから送信されてきたデータに基づいたレコードを生成し、それをデータベースに新規登録するようなことが可能となっています。

もう少し詳細に説明すると、CreateView はメソッドが GET のリクエストを受け取った際には get メソッドが実行され、メソッドが POST のリクエストを受け取った際には post メソッドが実行されるようになっています。この辺りが、CreateViewListView / DetailView との一番大きな違いになると思います。

CreateViewにgetメソッドとpostメソッドが存在する様子

で、この POST にも対応している分、CreateView を継承するクラスを扱うのは ListView や DetailView よりも難易度が若干高くなります。まぁでも、この辺りは関数ベースでビューを扱う場合も一緒だと思います。クラスベースでも関数ベースでも結局 POST に対応する分の労力が必要になります。

ですが、POST への対応に関して言えば、関数ベースビューよりもクラスベースビューの方が確実に楽に実現できると思います。関数ベースビューの場合は、関数の中でメソッドが GET であるか POST であるかを判断し、その判断結果に基づいて処理を切り変えるような実装を開発者自身が行う必要があります。

この辺りは、下記のフォームの解説ページで説明していますので、詳しく知りたい方は下記ページをご参照いただければと思います。

Djangoのフォームの解説ページアイキャッチ 【Django入門5】フォームの基本

それに対し、クラスベースビューの場合、メソッドが GET であるか POST であるかの判断を行い、それに応じて動作を切り替えるような処理が Django フレームワークの中で既に実装されています。そして、それがクラスとして定義されている例の1つが、今回扱う CreateView となります。したがって、開発者は、関数ベースビューでは必要だったメソッドに応じた処理の切り替えを実装する必要はありません。

CreateViewの中でメソッドに応じた処理の切り替えが実施される様子

つまり、次の CreateView でのクラスベースビューの作り方 で説明する手順で CreateView を継承するクラスを定義しておけば、あとはリクエストを受け取った際にリクエストに応じた処理が自動的に実行されるようになっています。そのため、CreateView を継承してクラスベースビューを作成することで処理の実装量を減らすことができますし、メソッドに応じた分岐処理等の実装も不要となるためバグも発生しにくくなります。

CreateView でのクラスベースビューの作り方

では、次はその CreateView でクラスベースビューを作成する手順について説明していきます。

CreateView でのクラスベースビューの作り方は、大まかに言えば ListView や DetailView 等の他の View のサブクラス の時と同様です。

要は、views.py に CreateView を継承するクラスを定義し、views.py に定義したクラスにクラス変数やメソッドを定義することで CreateView で定義されているクラス変数の上書きメソッドのオーバーライドを行なっていきます。これにより、CreateView の特徴を活かしながら自身のウェブアプリに応じたビューにカスタマイズしていくことが可能となります。

クラスベースビューの作り方の説明図

もう少し具体的に言えば、前述の通り、CreateView ではリクエストに応じて get メソッドや  post メソッドが実行されるようになっており、CreateView のクラス変数や他のメソッドは、これらの get メソッドおよび post メソッドから利用されるようになっています。したがって、CreateView を継承するクラス側でクラス変数やメソッドを上書き・オーバーライドしてやれば、この get メソッドと post メソッドの動作を変化させることができます。

CreateViewのgetメソッドやpostメソッドからクラス変数や他のメソッドが利用される様子

スポンサーリンク

利用するフォームを設定する

この CreateView でクラスベースビューを作成する場合にポイントになるのがクラス変数によるフォームクラスの設定になります。

まず、インスタンスの新規登録を実現するためには、get メソッドが実行された際に新規登録するインスタンス(レコード)の情報の入力・送信を受け付けるためのフォームを表示する必要があります。そして、このフォームはフォームクラスのインスタンスを HTML に要素として埋め込むことで表示されるようになっており、そのインスタンスのクラスを CreateView を継承するクラス側で設定しておく必要があります。

GETリクエストを受け取った時のCreateViewの動作

また、post メソッド実行時にもフォームクラスのインスタンスが必要となります。そのため、そのインスタンスの基になるフォームクラスを CreateView を継承するクラス側で設定しておく必要があります。

具体的にいうと、post メソッド実行時はフォームから送信されてきたデータを受け取り、そのデータからフォームクラスのインスタンスが生成されます。そして、このインスタンスに is_valid メソッドを実行させることで、送信されてきたデータの妥当性の検証が行われます。さらに、妥当性の検証結果が OK の場合は、そのインスタンスに save メソッドを実行させることで、送信されてきたデータがレコードとしてデータベースへ新規登録されることになります。

POSTリクエストを受け取った時のCreateViewの動作(妥当性の検証結果がOKの場合)

ここでポイントになるのがフォームクラスのインスタンスの save メソッドが実行されるという点になります。通常のフォームクラスには save メソッドが存在しないため、CreateView を継承するクラスで利用するのは単なるフォームクラスではなくモデルフォームクラスである必要があることになります。下記ページでも解説しているとおり、モデルフォームクラスは model に指定したモデルクラスに基づいて生成されるクラスであり、このクラスのインスタンスに save メソッドを実行させれば、その model に指定したモデルクラスに対応するテーブルにインスタンスが保存されることになります。

Djangoのモデルフォーム解説ページアイキャッチ 【Django入門8】モデルフォームの基本

ここで覚えておいていただきたいのは、データベースへの新規登録を行うためには、新規登録先のテーブルを指定する必要があり、それを決めるのがモデルフォームクラスになるという点になります。

post メソッド実行時の動作の説明に戻ると、post メソッドから妥当性の検証が行われて検証結果が NG の場合は、フォームクラスのインスタンスを再度 HTML に埋め込んだものをレスポンスとしてクライアントに返却することで、クライアント側で再度フォームの表示及び入力の再受付が行われるようになっています。

POSTリクエストを受け取った時のCreateViewの動作(妥当性の検証結果がNGの場合)

このような動作はフォームを扱う上での典型的な処理の流れとなっており、先ほども紹介した下記ページのフォームの解説ページでも詳しく説明していますので、復習したい方は是非下記ページを読んでみてください。

Djangoのフォームの解説ページアイキャッチ 【Django入門5】フォームの基本

このように、CreateView および CreateViewのサブクラスでは多くの場面でフォームクラスが利用されることになり、CreateView のサブクラスを定義する際にはフォームクラスの設定が非常に重要となります。

そして、CreateView のサブクラスで利用するフォームクラスは、下記のクラス変数を定義することで設定することができます。

  • クラス変数 form_class を定義する
  • クラス変数 modelfields を定義する

前者に関しては分かりやすくて、クラス変数 form_class を定義して CreateView のサブクラスで利用するフォームクラスを直接指定する実現方法になります。クラス変数 form_class を定義することで、form_class に指定したフォームクラスが CreateView のサブクラスから利用されるようになり、そのクラスのインスタンスがフォーム要素としてページに表示されたりデータベースへの保存を行なったりするようになります。

前述の通り、form_class にはモデルフォームクラスを指定する必要があります。モデルフォームクラスに関しては下記ページで解説していますので、モデルフォームクラスをご存知ない方は下記ページを参照してください。要は、モデルクラスに基づいて自動的に生成されるフォームクラスがモデルフォームクラスとなります。

Djangoのモデルフォーム解説ページアイキャッチ 【Django入門8】モデルフォームの基本

後者に関しては、CreateView の中で model で指定されたモデルクラスと fields で指定されたフィールドに基づいて新たにモデルフォームクラスを生成させ、そのフォームクラスを利用するように設定する実現方法になります。

model には新規登録したいインスタンス(レコード)のモデルクラスを指定し、fields にはフォームで扱いたいフィールドをリスト形式やタプル形式で指定します。この fields に指定するリストやタプルの要素は model に指定したモデルクラスの持つフィールドである必要があります。このようにクラス変数を定義することで、CreateViewget メソッドや post メソッドが実行される際には、fields に指定したリストやタプルに含まれるフィールドのみを持つモデルフォームクラスが生成されることになります。そして、それを利用して get メソッドや post メソッドが動作することになります。

modelとfieldsからフォームクラスが生成される様子

前者と後者のどちらの方法を採用しても CreateView のサブクラスはうまく動作させられることになりますが、モデルフォームクラスを定義しているのであれば前者の方法を、それ以外は後者の方法を採用するので良いと思います。

いずれにしても、この利用するフォームを設定する必要がある点は CreateView のサブクラスを使いこなす上で重要となりますので、是非この点は覚えておいてください。

リダイレクト先の URL を設定する

また、CreateView でクラスベースビューを作成する上ではリダイレクト先の URL の設定も必要となります。

CreateView ではフォームから送信されてきたデータに対して妥当性の検証が行われ、検証結果が OK の場合には送信されてきたデータがレコード(インスタンス)としてデータベースに保存されることになります。そして、保存完了後は他の URL へのリダイレクトが行われることになります。で、この時に、どの URL にリダイレクトするのかをクラス変数として定義しておく必要があります。もしくは、リダイレクト先の URL を返却するようなメソッドを定義しておく必要があります。これらが定義されていない場合は、下記のような例外が発生することになります。

No URL to redirect to.  Either provide a url or define a get_absolute_url method on the Model.

このような例外の発生は下記の3つの方法により回避することができます。

  • CreateView のサブクラスにクラス変数 success_url を定義する
  • CreateView のサブクラスにメソッド get_success_url を定義する
  • CreateView のサブクラスの model 等で指定したモデルクラスにメソッド get_absolute_url を定義する

リダイレクト先の URL が静的に決まるのであれば、1つ目の方法でリダイレクト先の URL を指定するようにしてやれば良いです。リダイレクト先の URL が静的に決まらないのであれば、つまり状況やタイミング等に応じてリダイレクト先の URL を動的に決めたいのであれば、2つ目 or 3つ目の方法でリダイレクト先の URL を取得できるようにする必要があります。

例えば、ユーザーの新規登録後に、そのユーザーのマイページにリダイレクトさせたいのであれば、新規登録されるユーザーのマイページの URL が新規登録されたユーザーに応じて変化することになるため、後者の方法でリダイレクト先の URL を取得できるようにする必要があります。

リダイレクト先をリクエストに応じて変化させる様子

ですが、ユーザーの新規登録後にユーザー一覧の表示ページにリダイレクトさせたいのであれば、ユーザー一覧の表示ページの URL は常に同じはずなので、前者の方法でリダイレクト先の URL を指定してやれば良いです。

リダイレクト先が毎回同じである様子

CreateView でクラスベースビューを作成する上では、インスタンス(レコード)の保存後にリダイレクトが行われることと、そのリダイレクト先の URL が指定できるようにクラス変数やメソッドの定義が必要であることは覚えておいてください。

妥当性の検証結果が OK の場合の処理を変更する

また、CreateView のサブクラスでは、フォームから送信されてきたデータの妥当性の検証結果が OK の場合に実行する処理をメソッドのオーバーライドによって変更可能であることも覚えておくと良いと思います。

CreateView では、フォームから送信されてきたデータの妥当性の検証結果が OK の場合に form_valid というメソッドが実行されるようになっています。この form_valid では、送信されてきたデータのレコードとしてのデータベースへの保存と前述のリダイレクトが行われるようになっています。逆に言えば、これらしか行われないようになっています。

ですが、このメソッドを CreateView のサブクラスに定義することでオーバーライドすることができ、妥当性の検証結果が OK の場合に実行する処理を追加したり変更したりすることが可能となります。

例えば、ログイン機能を備えたウェブアプリの場合、ユーザー登録後(ユーザーのインスタンスのデータベースへの新規登録後)にログイン処理も合わせて行いたいようなことがあります。このログイン処理の追加も、CreateView のサブクラスで form_valid をオーバーライドし、その form_valid の中でログイン処理を実行するようにすることで実現可能となります。

同様に、妥当性の検証結果が NG の場合には form_invalid が実行されるようになっているため、妥当性の検証結果が NG の場合の処理の追加や変更に関しても form_invalid のオーバーライドにより実現することができます。

ここで説明したような、利用するフォームクラスの設定方法、リダイレクト先の設定方法、妥当性の検証結果が OK の場合に実行する処理の変更方法は CreateView でクラスベースビューを作成する上では必ず覚えておいた方が良いポイントになりますし、この辺りさえしっかり理解しておけば、CreateView でのクラスベースビューの作成をスムーズに行えるようになると思います。

ここからは、ここで紹介したクラス変数やメソッドの詳細や具体的な定義・オーバーライドのやり方も含めて、CreateView で定義されているクラス変数・メソッドの紹介をしていきたいと思います。

スポンサーリンク

CreateView のクラス変数

では、次は CreateView で定義されているクラス変数の紹介を行なっていきます。これらのクラス変数を CreateView を継承するクラスで定義し直して上書きすることでクラスの動作のカスタマイズを行うことが可能となります。

CreateView の場合、GET メソッドのリクエストを受け取った際に get メソッドが実行され、GET メソッドのリクエストを受け取った際に post メソッドが実行されることになるため、これらの get メソッドや post メソッドの動作を変化させることを目的にクラス変数の定義を行なっていくことになります。

CreateView のクラス変数の一覧

その CreateView で定義されるクラス変数には下記のようなものが存在します。

extra_contexttemplate_engineresponse_classcontent_type に関しては ListView のクラス変数の役割と全く同じなので、詳しくは ListView の解説ページを参照していただければと思います(これら4つの各リンクには ListView の解説ページでの説明部分へのリンクを設定しています)。

また、これらが CreateView で定義されているクラス変数の全てというわけではないので注意してください。定義はされているものの CreateView では基本的に使用しないクラス変数は省き、さらにカスタマイズに利用する機会の多そうなもののみを紹介しています。全てのクラス変数を知りたい場合は、実際に CreateView の定義をソースコードで確認していただくのが一番早いと思います。

また、ここからは、アプリの models.py で下記のような Comment が定義されていることを、前提に解説を行なっていきます。

models.py
from django.db import models

class Comment(models.Model):
    text = models.CharField(max_length=256)
    date = models.DateField(auto_now_add=True)
    slug = models.SlugField(max_length=256, null=True, unique=True)

さらに、アプリの forms.py で下記のような CreateCommentForm が定義されていることを、前提に解説を行なっていきます。

forms.py
from django import forms
from .models import Comment

class CreateCommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['text', 'slug']

slug フィールドの役割や意味合いについては下記の DetailView の解説ページの中で説明していますので、必要に応じて下記ページを参照していただければと思います。

DjangoのDetailViewの解説ページアイキャッチ 【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現)

では、先ほど挙げたクラス変数の役割や、これらを定義することで実現できることについて、1つ1つ説明していきます。

form_class

最初に紹介するクラス変数が form_class で、CreateView でクラスベースビューを作成していく上で一番のポイントになるのが、この form_class になると思います。

この form_class はビューで扱うフォームクラスを指定するクラス変数になります。利用するフォームを設定する でも説明したように、form_class には単なるフォームクラスではなくモデルフォームクラスを指定します。

また、form_class で指定したフォームクラスは、これも 利用するフォームを設定する で説明したように、フォームを表示する際やフォームから送信されてきたデータをインスタンスとしてデータベースに保存するときなど、様々な場面で利用されることになります。

例えば、下記のように CommentCreate を定義すれば、CommentCreateget メソッドや post メソッド実行時に CreateCommentForm が利用されるようになります。

form_classの定義
from django.views.generic import CreateView
from .models import Comment
from .forms import CreateCommentForm

class CommentCreate(CreateView):
    form_class = CreateCommentForm

スポンサーリンク

success_url

前述の通り、CreateViewpost メソッドを持っており、post メソッド実行時にはフォームから送信されてきたデータのレコード(インスタンス)としてのデータベースへの保存が行われ、その後に特定の URL にリダイレクトをするようになっています(リダイレクトのレスポンスを返却する)。

success_url は、この時のリダイレクト先の URL を指定するためのクラス変数となります。

例えば、下記のように CommentCreate を定義すれば、CommentCreate の post メソッドが実行されてデータベースへのインスタンスの保存が行われた後に 'comments' という名前が付けられた URL にリダイレクトされるようになります。

get_successの定義
from django.views.generic import CreateView
from .models import Comment
from .forms import CreateCommentForm
from django.urls import reverse_lazy

class CommentCreate(CreateView):
    form_class = CreateCommentForm
    success_url = reverse_lazy('comments')

ウェブアプリがリクエストを受け付ける URL に対しては urls.py によって名前をつけることができます。そして、その名前から URL に変換するのが、上記で利用している reverse_lazy となります。

reverse_lazy については下記ページで解説していますので、詳しくは下記ページをご参照ください。success_url に URL を指定する際には reverse ではなく reverse_lazy を利用する必要がある点がポイントとなります。

reverseとreverse_lazyの違いの解説ページアイキャッチ 【Django】reverseとreverse_lazyの違い

また、クラス変数 success_url に指定できるのは静的な URL のみとなります。状況に応じてリダイレクト先の URL を動的に変化させたいような場合は get_success_url メソッドのオーバーライドを行う or モデルクラスに get_absolute_url を定義する必要があります。この詳細については、後述の get_success_url で説明します。

model

また、利用するフォームを設定する で説明したように、クラス変数 form_class を定義するのではなく、ここで説明する model と次に説明する fields をクラス変数として定義することでも CreateView のサブクラスが利用するフォームを設定することが可能です。

クラス変数 form_class を指定した場合は、form_class で指定されたクラスのインスタンスがフォームとして利用されることになります。それに対し、modelfields を指定した場合は、これらにセットされたモデルクラスとフィールドからフォームクラスオブジェクトが生成され、そのインスタンスがフォームとして利用されることになります。

ListViewDetailView ではインスタンスの取得先のテーブルを指定するために model を定義しましたが、CreateView の場合はインスタンスの取得先のテーブルを指定するのではなく、form_class が定義されていない場合に利用するフォームを生成するために定義することになります。また、後述でも説明しますが、クラス変数 template_name を定義しない場合も、この model の定義が必要になります。

逆に言えば、form_classtemplate_name が定義されているのであれば、model の定義はあってもなくても良いです。

MEMO

また、詳細な説明は省略しますが、model の代わりに queryset を定義することも可能です

クエリには操作対象となるテーブル(モデルクラス)の情報が含まれており、その情報から特定したモデルクラスが model の代わり利用されることになります

例えば、Comment の持つフィールドに応じたフォームを CreateView を継承するクラスから利用するようにしたいのであれば、下記のように model を定義してやれば良いことになります。

modelの定義
from django.views.generic import CreateView
from .models import Comment
from .forms import CreateCommentForm

class CommentCreate(CreateView):
    model = Comment

ただし、単に上記のように model を定義しただけだと次のような例外が発生することになります。

Using ModelFormMixin (base class of CommentCreate) without the 'fields' attribute is prohibited.

この例外のメッセージからも分かるように、クラス変数 model だけでなくクラス変数 fields も一緒に定義してやれば、この例外は解決することができます。

つまり、CreateView のサブクラスにおいて、扱うフォームをクラス変数  form_class ではなくクラス変数 model で設定する場合はクラス変数 fields の定義が必須となります。

fields

では、続いてはその fields について説明していきます。

fields は、フォームに表示するフィールドを指定するクラス変数となります。model に指定するのはモデルクラスで、このモデルクラスはデータベースのテーブルにあたり、このテーブルは models.py でのモデルクラスの定義に基づいてフィールド(カラム)を持つことになります。

fields は、このテーブルのフィールドの中のどれをフォームのフィールドとして扱うのかを指定するクラス変数となります。

modelとfieldsからフォームクラスが生成される様子

そして、modelfields を定義しておけば、利用するフォームを設定する で説明したように、これらによって指定されたフィールドの入力受付を行うフォーム(モデルフォームクラス)が定義され、そのフォームが CreateView のサブクラスで扱われるようになります。

例えば下記のように CommentCreate を定義すれば、Commenttextslug をフィールドとして持つモデルフォームクラスが生成され、それを CommentCreate が利用するようになります。

fieldsの定義
from django.views.generic import CreateView
from .models import Comment
from .forms import CreateCommentForm

class CommentCreate(CreateView):
    model = Comment
    fields = ['text', 'slug']

したがって、CommentCreate でフォームを表示する際には、下の図のように text と slug の入力受付を行うフォームが表示されるようになります。

CommentCreateによって表示されるフォーム

このフォームの表示を行うためにはテンプレートファイルの用意であったり、利用するテンプレートファイルを指定するクラス変数の定義が必要となります。この辺りは後述で解説していきます。

前述の通り、modelfields はセットで定義する必要があります。逆に、form_class を定義する場合には fields の定義は禁止されており form_classfields の両方をクラス変数として定義すると下記のような例外が発生することになるので注意してください。この場合は fields の定義を削除してやれば例外を解消することができます。

Specifying both 'fields' and 'form_class' is not permitted.

スポンサーリンク

initial

initial はフォームの各種フィールドの初期値を指定するクラス変数となります。このクラス変数 initial には、下記のように辞書形式のデータを指定する必要があります。

initialへの指定値
initial = {
    'フィールド1のフィールド名': フィールド1の初期値,
    'フィールド2のフィールド名'; フィールド2の初期値
}

クラス変数 initial でフォームの各種フィールドに初期値を指定しておけば、フォームが表示された際に、各種フィールドに初期値に指定した値が表示されるようになります。

例えば下記のように CreateView を定義すれば、クラス変数 initial により、GET メソッドのリクエストによってフォームが表示される際に text フィールドには hello という文字列が入力された状態で表示されることになります。それに対し slug フィールドには初期値を指定していないため、slug フィールドは空白の状態で表示されることになります。

initialの定義
from django.views.generic import CreateView
from .models import Comment
from .forms import CreateCommentForm

class CommentCreate(CreateView):
    model = Comment
    fields = ['text', 'slug']
    initial = {
        'text': 'hello'
    }

実際のフォームの表示結果は下記のようなものになります。

クラス変数initialの定義によってtextフィールドに初期値が設定されている様子

prefix

prefix は各種フィールドのタグの属性に指定する文字列に接頭辞を付加するためのクラス変数となります。

例えば、先ほどの initial の説明の中で示した CommentCreate の例の場合、生成される HTML において、フォームの中の text フィールドと slug フィールドのタグは下記のようなものになります。

prefixなしの場合のタグ
<tr>
  <th><label for="id_text">Text:</label></th>
  <td>
    <input type="text" name="text" value="hello" maxlength="256" required id="id_text">
  </td>
</tr>
<tr>
  <th><label for="id_slug">Slug:</label></th>
  <td>
    <input type="text" name="slug" maxlength="256" required id="id_slug">
  </td>
</tr>

それに対し、下記のようにクラス変数 prefix を定義した場合、

prefixの定義
from django.views.generic import CreateView
from .models import Comment
from .forms import CreateCommentForm

class CommentCreate(CreateView):
    model = Comment
    fields = ['text', 'slug']
    initial = {
        'text': 'hello'
    }
    prefix = 'comment'

生成される HTML において、フォームの中の text フィールドと slug フィールドのタグは下記のようなものに変化します。ところどころ、属性に指定される文字列に prefix に指定した comment が付加されていることが確認できると思います。一部接頭辞ではないものもありますが…。まぁとりあえず、こんな感じで各属性への指定値に特定の文字列を追加で付加するためのクラス変数が prefix となります。

prefixありの場合のタグ
<tr>
  <th><label for="id_comment-text">Text:</label></th>
  <td>
    <input type="text" name="comment-text" value="hello" maxlength="256" required id="id_comment-text">
  </td>
</tr>
<tr>
  <th><label for="id_comment-slug">Slug:</label></th>
  <td>
    <input type="text" name="comment-slug" maxlength="256" required id="id_comment-slug">
  </td>
</tr>

template_name

ここからは、テンプレートファイルに関するクラス変数について説明していきます。CreateView の場合、GET メソッドのリクエストを受け取ってフォームを表示する際にテンプレートファイルが利用されます。

GETメソッドのリクエストを受け取った際にテンプレートファイルからHTMLを生成する様子

また、POST メソッドのリクエストを受け取った場合でも、フォームから送信されてきたデータの妥当性の検証の検証結果が NG の場合には再度フォームを表示することになるため、その際にもテンプレートファイルが利用されることになります。

POSTメソッドのリクエストを受け取って妥当性の検証がNGであった際にテンプレートファイルからHTMLを生成する様子

こういったページの表示を行う際に利用するテンプレートファイルのパスを指定するのがクラス変数 template_name となります。このパスには、アプリの templates フォルダから見た相対パスを指定します。

デフォルトでは、テンプレートファイルのパスとして アプリ名/モデルクラス名_form.html が設定されるようになっているため、CreateView を継承するクラスが動作する際は、あらかじめこのパスにテンプレートファイルを用意しておくか、template_name を定義して template_name に指定したパスにテンプレートファイルを用意しておく必要があります。

例えば、forum というアプリの views.py で下記のように CommentCreate を定義した場合、

template_nameの定義
from django.views.generic import CreateView
from .models import Comment
from .forms import CreateCommentForm

class CommentCreate(CreateView):
    form_class = CreateCommentForm
    template_name = 'forum/create.html'

テンプレートファイルは forum/templates/forum/create.html に用意しておく必要があります。また、これに関しては後述の CreateView が生成するコンテキスト で説明しますが、CreateView が生成するコンテキストには form キーに form_class で指定したフォームクラスのインスタンス or model + fields から生成されるクラスのインスタンスがセットされることになるため、用意するテンプレートファイルでは form 変数をフォームとして表示するようにしておく必要があります。

テンプレートファイルのform参照箇所にフォームが埋め込まれる様子

スポンサーリンク

template_name_suffix

先ほど template_name を定義しなかった場合、テンプレートファイルのパスとして アプリ名/モデルクラス名_form.html がデフォルトで設定されると言いましたが、この _form 部分はクラス変数 template_name_suffix を定義して変更することも可能です。また、モデルクラス名 部分は model に指定したモデルクラスのクラス名を小文字にしたものになります。

template_name_suffix は、その名の通りデフォルトのテンプレートファイルのパスにおけるファイル名の末尾を指定するクラス変数であり、CreateView の場合はデフォルトで '_form' が指定されています。

例えば、forum というアプリの views.py で下記のように CommentCreate を定義した場合、

template_nameの定義なし
from django.views.generic import CreateView
from .models import Comment
from .forms import CreateCommentForm

class CommentCreate(CreateView):
    model = Comment
    fields = ['text', 'slug']

template_name を指定していないため、デフォルト設定のテンプレートファイルが利用されることになり、そのファイルのパスは forum/comment_form.html となりますので、このパスにテンプレートファイルを用意しておく必要があることになります。

それに対し、下記のように CommentCreate を定義した場合、

template_nameの定義
from django.views.generic import CreateView
from .models import Comment
from .forms import CreateCommentForm

class CommentCreate(CreateView):
    model = Comment
    fields = ['text', 'slug']
    template_name_suffix = '_create'

デフォルト設定のテンプレートファイルが利用されるという点は先ほどと同じですが、template_name_suffix'_create' を指定しているため、利用されるテンプレートファイルのパスは forum/comment_create.html となります。

template_name を定義しない場合、利用するテンプレートファイルのパスが template_name_suffix だけでなく model に指定したモデルクラスのクラス名を参照して決定されるという点もポイントとなります。つまり、テンプレートファイルのパスを決定するためには model を指定してやる必要があります。template_name を定義せず、さらに model も定義しなかった場合、下記のような例外が発生することになるので注意してください。

TemplateResponseMixin requires either a definition of 'template_name' or an implementation of 'get_template_names()'

この例外は、クラス変数 model を定義する(queryset でもオーケー)、もしくはクラス変数 template_name を定義することで解消することができます。

CreateView のメソッド

次に CreateView の持つメソッドを紹介していきます。CreateView を継承したクラスを定義し、そのクラスで CreateView の持つメソッドをオーバーライドしてやることで CreateView とは異なる動作のクラスを実現することができるようになります。

まずはメソッド一覧を示し、続いて、その中からオーバーライドする機会が多そうなメソッドのみの詳細を説明するようにしたいと思います。

CreateView のメソッド一覧

CreateView の持つメソッドの一覧は下記のようになります。あくまでもクラスのカスタマイズ目的でオーバーライドを行う可能性のあるものを挙げており、as_view などのカスタマイズは行わないであろうメソッドは省略しています。

また、CreateViewget メソッドと post メソッドを持っており、メソッドが GET のリクエストを受け取った場合は get が実行され、メソッドが POST のリクエストを受け取った場合は post が実行されようになっています。そして、これらの getpost の中から各種メソッドが実行されるようになっています。

  • get:リクエストのメソッドが GET の場合の処理を実行する
  • post:リクエストのメソッドが POST の場合の処理を実行する
  • get_context_data:テンプレートに渡すコンテキストを生成する
  • get_form:フォームクラスのインスタンスを取得する
  • get_form_class:フォームクラスを取得する
  • get_form_kwargs:フォームクラスのコンストラクタに指定するパラメータを取得する
  • get_initialinitial への指定値を取得する
  • get_prefixprefix への指定値を取得する
  • form_valid:フォームから送信されてきたデータをデータベースに保存して特定の URL へのリダイレクトレスポンスを返却する(妥当性の検証が OK の時に実行される)
  • form_invalid:再度フォームを表示する(妥当性の検証が NG の時に実行される)
  • get_success_url:リダイレクト先の URL を取得する
  • get_template_names:テンプレートファイルの名前を取得する
  • render_to_response:レスポンスを返却する

get メソッドの処理の流れ

get メソッドと post メソッドの処理について簡単に説明しておくと、まず get では get_context_data メソッドが呼び出され、このメソッドの中でフォームクラスのインスタンスが生成され、それがコンテキストの 'form' キーに値としてセットされることになります。フォームクラスのインスタンスは get_form メソッドで生成されるようになっており、

この get_form メソッドの中では最初に get_form_class が呼び出され、クラス変数 form_class で指定されたクラス or modelfields によって生成されるクラスが取得されます。さらに、get_form_kwargs で、それらのクラスのコンストラクタに指定するキーワード引数が取得されます。この中でクラス変数 initial やクラス変数 prefix が取得され、それがキーワード引数に指定された状態でコンストラクタが実行されることで、これらのクラス変数への指定値が反映された状態のフォームクラスのインスタンスが生成されることになります。

そして、このフォームクラスのインスタンスがコンテキストの 'form' キーに値としてセットされます。コンテキスト生成後には render_to_response メソッドが実行され、コンテキストとテンプレートファイルに基づいて HTML が生成されてレスポンスとして返却されることになります。

ちなみに、下記ページで紹介している “インスタンスの更新を行う View のサブクラス” である UpdateView においては、前述の get_form_kwargs で事前にデータベースから取得した更新対象のインスタンスが 'instance' キーにセットされた辞書が取得されるようになっています。そして、それを引数としてコンストラクタが実行されてフォームクラスのインスタンスが生成されることになります。

UpdateViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】UpdateViewの使い方(クラスベースビューでのレコード更新ページの実現)

したがって、UpdateView では get メソッド実行時に事前に取得されたインスタンスの情報が各種フィールドにセットされた状態でフォームが表示されることになりますし、後述の post メソッド実行時には事前に取得されたインスタンスをフォームから送信されてきたデータで上書きしたものがデータベースに保存されることでインスタンス(レコード)の更新が実現されるようになっています。

こういった違いはあるものの、基本的には、UpdateViewCreateView では事前にインスタンスをデータベースから取得するかどうかと、get_form_kwargs が返却する辞書に事前に取得したインスタンスがセットされているかどうかの違いしかありません。そのため、CreateView をしっかり理解しておけば UpdateView も簡単に使いこなすことができます!

post メソッドの処理の流れ

それに対し、post メソッドでは、まず get_form メソッドが実行されてフォームのインスタンスの生成が行われます。このフォームのインスタンスがコンストラクタで生成される際にはコンストラクタの引数にフォームから送信されてきたデータが指定されることになるため、各種フィールドにフォームから送信されてきたデータがセットされた状態のインスタンスが生成されることになります。

さらに、生成されたインスタンスに is_valid メソッドを実行させることで、送信されてきたデータの妥当性の検証が行われます。この is_valid メソッドはフォームクラスの持つメソッドになります。

妥当性の検証結果が OK の場合は form_valid メソッドが実行されます。form_valid メソッドでは、まずフォームクラスのインスタンスから save メソッドが実行され、送信されてきたデータがレコードとしてデータベースに保存されることになります。そして、get_success_url メソッドが返却する URL に対するリダイレクトレスポンスが生成されて返却されることになります。get_success_url メソッドではクラス変数 success_url が定義されている場合はそれが返却され、それ以外の場合は、保存されたレコード(インスタンス)から get_absolute_method が実行されるようになっており、このメソッドの返却値がリダイレクト先の URL として返却されることになります。

妥当性の検証結果が NG の場合は form_invalid メソッドが実行され、この場合は get メソッドの時と同様に HTML が生成されてレスポンスとして返却されることになります。ただし、コンテキストの 'form' キーに値としてセットされるフォームクラスのインスタンスには、フォームから送信されてきた各種フィールドのデータおよび妥当性の検証結果が NG となった理由を示すエラーメッセージがセットされた状態となっているため、このインスタンスをフォームとして表示した際には、各種フィールドにユーザーが前回入力したデータがセットされた状態で、さらにはエラーメッセージを含む状態でフォームが表示されることになります。

この辺りの妥当性の検証であったり、妥当性の検証結果が NG となった場合のフォームの際表示に関しては下記ページで詳しく説明していますので、必要に応じて参照していただければと思います。

Djangoのフォームの解説ページアイキャッチ 【Django入門5】フォームの基本

簡単に説明させていただきましたが、get メソッドや post メソッドでは上記のような流れで処理が行われており、その中で様々なメソッドの実行やクラス変数の参照が行われるようになっています。

これらのメソッドの中で、特にオーバーライドする機会が多くなるのは form_validget_success_url になると思います。この2つについて詳細を説明していきたいと思います。

スポンサーリンク

form_valid

まず form_valid について説明していきます。

form_valid はフォームから送信されてきたデータの妥当性の検証結果が OK である場合に実行されるメソッドで、このメソッド内部ではフォームから送信されてきたデータをレコードとしてデータベースに保存し、さらに特定の URL にリダイレクトするためのレスポンスを返却する処理が行われるようになっています。

ちなみに、Django においてレコードとはインスタンスのことであり、form_valid の中では、form_class で指定されるモデルフォームのベースとなるモデルクラスのインスタンス or model で指定されるモデルクラスのインスタンスがデータベースに保存されることになります。また、保存先のテーブルは上記のモデルクラスに対応するテーブルとなります(Django においては、モデルクラスがデータベースにおけるテーブルを表しています)。

form_valid ではこれらの処理が自動的に行われるようになっているのですが、逆に言えば、これら以外の処理は行われないため、妥当性の検証結果が OK の時の処理を変更したい場合は、form_valid のオーバーライドを行う必要があります。

MEMO

ここでは詳細な説明は省略しますが、レコードの保存はフォームクラスの save メソッドで実行されるため、そういった CreateView から継承されるメソッド以外のメソッドで他の処理を追加することも可能です

例えば、ログイン機能を備えたアプリを開発している際に、ユーザーの新規登録が行われた際にログイン処理も一緒に実行したいような場合があります。ユーザーの新規登録はデータベースへのレコードの保存によって実現されるため、form_valid によってユーザーの新規登録を行うことは可能です。ですが、form_valid ではその後にリダイレクトのレスポンスを返却して処理を終了するようになっているため、CreateViewform_valid ではユーザーの新規登録後のログイン処理を実現することができません…。

ですが、CreateView のサブクラス側で form_valid をオーバーライドしてやれば、post から form_valid が呼び出しされる際には CreateView のサブクラス側の form_valid が実行されるようになり、そこで様々な処理を追加で行うようなことが可能となります。

例えば下記は、【Django入門14】クラスベースビューの基本掲示板アプリのビューをクラスベースに変更する で紹介した form_valid のオーバーライドの例になります。

下記のように form_valid を定義してオーバーライドしてやれば、スーパークラスの form_valid、つまり CreateViewの form_valid が実行されてデータベースへのレコードの保存とリダイレクトのレスポンスを生成したのちに login が実行され、ここでログイン処理をが行われることになります。

form_validのオーバーライド
class Register(CreateView):
    model = User
    form_class = RegisterForm
    template_name = 'forum/register.html'

    def form_valid(self, form):
        response = super().form_valid(form)
        user = self.object
        login(self.request, user)
        return response

form_valid は基本的にリダイレクトのレスポンスを返却する必要がある点に注意してください。CreateViewの form_valid の返却値もリダイレクトのレスポンスとなりますので、その返却値をオーバーライドした側の form_valid でも返却するようにしてやればリダイレクトのレスポンスの返却を実現することができることになります。上記においては return response の部分がそれにあたります。

今回はログイン処理の例を示しましたが、他にも様々な処理を form_valid に追加することが可能です。また、フォームから送信されてきたデータの妥当性の検証結果が NG の場合は form_invalid メソッドが実行されるようになっているため、この場合の動作を変更したい場合は、ここで紹介した form_valid と同様に form_invalid のオーバーライドを行えば良いです。

get_success_url

次は get_success_url のオーバーライドの例を紹介していきます。

get_success_url はリダイレクト先の URL を取得するメソッドで、このメソッドは先ほどオーバーライドの例を示した form_valid から呼び出されるようになっています。

さらに、この get_success_url では、クラス変数 success_url が定義されている場合は、その success_url に指定されている URL が返却されることになります。また、クラス変数 success_url が定義されていない場合、かつ、データベースに保存されたインスタンスのモデルクラスに get_absolute_url が定義されている場合は、その get_success_url の返却値が返却されることになります。

なので、基本的にはクラス変数 success_url を定義してリダイレクト先の URL を指定するので良いです。ですが、クラス変数の場合、アプリ起動時に静的にクラス変数への指定値が決まることになりますので、動的にリダイレクト先の URL を変化させたいような場合はクラス変数の定義ではなくメソッドで実現する必要があります。その具体的な方法の1つが get_success_url のオーバーライドとなります。

MEMO

前述の通り、クラス変数 success_url が定義されていない場合、モデルクラス側の get_absolute_url メソッドが呼び出されることになります

したがって、モデルクラス側の get_absolute_url を定義して動的にリダイレクト先の URL を生成して返却するようにすることも可能です

これに関しては、後述の CreateView の利用例 で補足します

例えば下記は、【Django入門14】クラスベースビューの基本掲示板アプリのビューをクラスベースに変更する で紹介した get_success_url のオーバーライドの例になります。

下記のように get_success_url を定義してオーバーライドしてやれば、post メソッドからは CreateViewget_success_url ではなく、Registerget_success_url が呼び出されるようになります。

get_success_urlのオーバーライド
class Register(CreateView):
    model = User
    form_class = RegisterForm
    template_name = 'forum/register.html'

    def get_success_url(self):
        return reverse('user', kwargs={'user_id':self.object.pk})

さらに、上記の get_success_url では reverse 関数の kwargs'user_id' のキーの値として self.object.pk を指定しているため、リダイレクト先の URL として、データベースに新規登録した User クラスのインスタンスに割り当てられたプライマリーキーに対応した URL が返却されることになります。このプライマリーキーはインスタンスがデータベースに新規登録されるタイミングで毎回異なるものがインスタンスに割りられるフィールドです。そのため、インスタンスの新規登録のたびに、そのインスタンスのプライマリーキーに応じて毎回異なる URL が返却されることになり、動的にリダイレクト先の URL が変化することになります。

CreateView が生成するコンテキスト

続いて CreateView が生成するコンテキストについて説明しておきます。

CreateView が生成するコンテキストには下記の要素が含まれます。見てわかる通り、基本的に CreateView が生成するコンテキストは 'form' キーのみを持つことになります。

  • 'form' : フォームクラスのインスタンス

ひとまずフォームを表示するだけであれば、テンプレートファイルから form を出力するようにしてやれば良いことになります。他のデータをテンプレートファイルから参照したい場合は、extra_context で説明しているクラス変数 extra_context を定義したり、get_context_data のオーバーライドを行なったりしてコンテキストの要素を追加する必要があります。

スポンサーリンク

CreateView の利用例

最後に、ここまでの説明のまとめとして、CreateView を継承するクラスでのビューの作成例を示していきたいと思います。

プロジェクトとアプリの作成やモデルの定義・マイグレーションの手順に関しては下記ページの ListView の利用例 と同じとなりますので、ここでは説明を省略させていただきます。ListView の解説ページの利用例を読んでいない方は、下記ページの プロジェクトとアプリの作成モデルの作成とマイグレーション を読んで手順を進めていただければと思います。

DjangoのListViewの解説ページアイキャッチ 【Django】ListViewの使い方(クラスベースビューでの一覧リストページの実現)

フォームの定義

プロジェクトやアプリ、モデルの準備が済んだ後は、フォームクラスの定義を行なっていきたいと思います。CreateView のサブクラスでクラス変数 modelfields の定義を行なっておけばフォームクラスは必須ではないのですが、今回の例ではフォームクラスを定義し、そのフォームクラスを CreateView のサブクラスで利用するようにしていきたいと思います。

ということで、forms.py を作成し、そこにフォームクラスの定義を行なっていきたいと思います。まず、forum フォルダの下に forms.py というファイルを新規作成し、その forms.py の中身を下記のように変更して保存してください。

forms.py
from django import forms
from .models import Comment

class CreateCommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['text', 'slug']

この CreateCommentFormmodel には Comment を定義しているため、Comment モデルクラスに基づいたフォームクラスが定義されることになります。したがって、この CreateCommentForm のインスタンスから save メソッドを実行させた場合は、Comment モデルクラスの save メソッドが実行されて Comment モデルクラスのインスタンスがレコードとしてデータベースに保存されることになります。そして、この CreateCommentForm のインスタンスからの save メソッドの実行は次に説明する CommentCreate (CreateView) の form_valid から行われるようになっています。

各クラスのメソッドが実行されてデータベースへの保存が行われる様子

さらに fields = ['text', 'slug'] を指定しているため、フォームクラスのインスタンスを表示した際には、Comment モデルクラスに定義されているフィールドのうち、textslug のみの入力受付を行うフォームが表示されることになります。

こんな感じで、モデルクラスに基づいたフォームクラスがモデルフォームクラスとなります。

クラスベースビューの作成

続いて、このページの主題ともなっているクラスベースビューを作成していきます。今回は、ここまでの説明の通り CreateView を継承するクラスを定義し、そのクラスをクラス変数の定義とメソッドのオーバーライドによってカスタマイズしていきます。

今回は、下記のように views.py を変更することでクラスベースビューを作成したいと思います。

views.py
from django.views.generic import  DetailView, CreateView
from .models import Comment
from .forms import CreateCommentForm
from django.urls import reverse


class CommentCreate(CreateView):
    form_class = CreateCommentForm
    initial = {
        'text': 'hello'
    }
    template_name = 'forum/commentcreate.html'

    def get_success_url(self):
        return reverse('comment', kwargs={'comment_id':self.object.id})

class CommentDetail(DetailView):
    model = Comment
    pk_url_kwarg = 'comment_id'
    template_name_suffix = 'info'

この views.py においては2つのクラスを定義しており、一方は DetailView を継承するクラスとして CommentDetail を定義しています。このクラスは、CommentCreatepost メソッドが実行された際のリダイレクト先として利用するために定義しています。

DetailView に関しては下記ページで詳細を解説していますので、詳しく知りたい方は下記ページを参照してください。

DjangoのDetailViewの解説ページアイキャッチ 【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現)

また、もう1つのクラスが、このページの主題となっている CreateView を継承するクラスとなります。このクラスでは form_classCreateCommentForm を指定しているため、フォームを扱う際には CreateCommentForm が利用されることになります。

また、CreateCommentFormmeta 属性の modelComment を指定しているため、このフォームから送信されてきたデータをレコードとして保存する際には、Comment に対応するテーブルに保存が行われることになります。こんな感じで、CreateView を継承するクラスでは様々なクラスが連携して動作することになるところも CreateView のポイントの1つになると思います。そして、様々なクラスが連携して動作することになるため、CreateView のカスタマイズだけでなく、フォームクラスやモデルクラスの変更によってビューの動作を変更することも可能となります。

例えば、上記の CommentCreate では get_success_url をオーバーライドしています。このオーバーライドによる効果は get_success_url で既に解説したつもりなのでここでの詳細な説明は省略しますが、下記のように、Comment クラスに get_absolute_url を定義しておくことでも同様のことを行うことが可能です。

get_absolute_urlの定義
def get_absolute_url(self):
        return reverse('comment', kwargs={'comment_id': self.id})

CreateViewget_success_url ではクラス変数 success_url が定義されていない場合に事前にデータベースに保存されたインスタンスの get_absolute_url 実行結果が get_success_url の返却値として扱われるようになっています。今回の例であれば、CommentCreate によってデータベースに保存されるインスタンスは Comment クラスのものとなるため、Commentget_absolute_url の実行結果が get_success_url の返却値、すなわちリダイレクト先の URL として扱われることになります。そのため、get_absolute_url で動的に URL を生成して返却するようにしておけば、get_success_url のオーバーライドを行わなくてもリダイレクト先の URL を動的に変更することが可能となります。

スポンサーリンク

ビューと URL とのマッピング

少し横道にそれましたが、次は views.py で定義したビューと URL とのマッピングを行なっていきます。

まず、classviewproject フォルダの下にある urls.py を下記のように変更してください。

classviewproject/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('forum/', include('forum.urls')),
]

続いて、forum フォルダの下に urls.py を新規作成し、中身を下記のように変更してください。

forum/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('comment/<int:comment_id>/', views.CommentDetail.as_view(), name='comment'),
    path('create/', views.CommentCreate.as_view(), name='create'),
]

これらの urls.py により、ルートパス指定で /forum/create/ へのリクエストがあった際に CommentCreateget メソッドが実行されてフォームが表示されるようになります。そして、フォームからデータが送信されてきた際には post メソッドが実行され、さらにはデータベースに保存されたインスタンスのプライマリーキーを comment_id として 'comment' の名前が付けられた URL にリダイレクトされます。それにより、次は CommentDetailget メソッドが実行されることになり、その保存されたインスタンスの詳細情報が表示されることになります。

テンプレートの作成

次はテンプレートファイルを作成します。ソースコードの変更としては、これが最後となります。

CommentCreate が利用するテンプレートファイル

まず、CommentCreate ではクラス変数 template_name を定義し、template_name には 'forum/commentcreate.html' を指定しているため、アプリ内の templates フォルダから見た相対パスが 'forum/commentcreate.html' となる位置に存在するテンプレートファイルが利用されることになります。

このパスにテンプレートファイルを用意しておく必要があるため、forum フォルダの中にまず templates フォルダを作成し、さらに templates の中に forum フォルダを作成してください。

さらに、最後に作成した forum フォルダの中に commentcreate.html を新規作成し、中身を下記のように変更して保存してください。

commentcreate.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of CreateView</title>
</head>
<body>
    <main>
        <h2>コメントの投稿</h2>
        <form action="{% url 'create' %}" method="post">
            {% csrf_token %}
            <table>{{ form.as_table }}</table>
            <p><input type="submit" value="送信"></p>
        </form>
    </main>
</body>
</html>

このテンプレートファイルでは変数として form を参照しています。で、CreateView が生成するコンテキストで説明したように、CreateView で生成されるコンテキストには 'form' キーとして form_class で指定されたフォームクラスのインスタンス or model + fields から生成されるフォームクラスのインスタンスがセットされることになるため、このテンプレートファイルでは form を参照することでフォームクラスのインスタンスが扱えることになります。そして、{{ form.as_table }} と記述しておけば、そのフォームクラスに定義されているフィールドがテーブル形式で HTML に埋め込まれることになります。そして、それがページとして表示されることで、それらの入力フィールドを含むフォームが表示されることになります。

CommentDetail が利用するテンプレートファイル

また、CommentDetail で利用されるテンプレートファイルに関しては下記ページで紹介していますので、これに関しては下記ページをご参照いただければと思います。

DjangoのDetailViewの解説ページアイキャッチ 【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現)

結論としては、先ほど最後に作成した forum フォルダの下に commentinfo.html を新規作成し、中身を下記のように変更して保存してやれば良いです。

commentinfo.html
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Example of DetailView</title>
</head>
<body>
    <main>
        <h2>コメント({{ comment.id }})</h2>
        <table>
            <tbody>
                <tr><td>ID   :</td><td>{{ comment.id }}</td></tr>
                <tr><td>TEXT :</td><td>{{ comment.text }}</td></tr>
                <tr><td>DATE :</td><td>{{ comment.date }}</td></tr>
                <tr><td>SLUG :</td><td>{{ comment.slug }}</td></tr>
            </tbody>
        </table>
    </main>
</body>
</html>

動作確認

最後に動作確認を行なっておきましょう!

まずは、manage.py が存在するフォルダで下記コマンドを実行します。

% python manage.py runserver

これにより、開発用ウェブサーバーが起動しますので、ウェブブラウザから作成したアプリのページを表示することが可能となります。

次に下記 URL をウェブブラウザで表示してみてください。

http://localhost:8000/forum/create/

この URL をウェブブラウザで表示すれば下図のような textslug の入力フィールドを持つフォームが表示されるはずです。

ブラウザに表示されるフォーム

ここで表示されるフォームは CreateCommentForm で定義されるクラスのインスタンスであり、CreateCommentFormMeta 属性の fields['text', 'slug'] を、modelComment を指定しているため、Commenttextslug フィールドを入力するためのフォームが表示されることになります。

また、text フィールドに hello とあらかじめ入力されているのは CommentCreate クラスのクラス変数として initial ={'text': 'hello'} を指定しているからになります。

表示されたフォームで適当な半角英数字の文字列を textslug に入力して 送信 ボタンを押せば、これらのフィールドに入力されたデータが対応するフィールドにセットされ、さらに id(プライマリーキー) と date(新規作成された日時)  が自動的にセットされた Comment のインスタンスがデータベースに新規登録されることになります。ただし、slug に関してはデータベースに保存されている他の Comment のインスタンスと重複していると妥当性の検証結果が NG になるようになっているので注意してください。

送信 ボタンを押してインスタンスがデータベースに新規登録されれば、リダイレクトが行われ、そのインスタンスの詳細ページに自動的に遷移するようになっているはずです。

リダイレクトされて表示されるページ

このリダイレクト先の URL は CommentCreate クラスの get_success_url によって決定され、そのリダイレクト先の URL は下記となります。

http://localhost:8000/forum/comment/<int:comment_id>/

この <int:comment_id> には、フォームの送信によって新規保存されたインスタンスのプライマリーキーが自動的に設定されてリダイレクトされるようになっています。

こんな感じで、CreateView を継承するクラスを定義してクラス変数やメソッドを適切に定義してやれば、簡単にインスタンス(レコード)の新規登録を行うビューを実現することができます。モデルクラスの定義やモデルフォームクラスの定義、さらにはクラス変数やメソッドの定義を変えてみて、その時に動作がどう変わるのかを確認していけば、もっと CreateView への理解が深まると思いますので、是非これらにも挑戦してみてください!

スポンサーリンク

まとめ

このページでは、CreateView および CreateView を継承したクラスベースビューの作り方について解説しました!

CreateView はインスタンス(レコード)をデータベースに新規登録するための View のサブクラス であり、このクラスを継承することでユーザー登録やコメントの新規投稿等を行うページを簡単に実現できます。

CreateView のポイントの1つはフォームを扱うという点であり、そのためにフォームクラスの定義やフォームクラスに関するクラス変数の定義等が必要になります。

また、POST メソッドのリクエストを扱うことができるという点もポイントとなります。関数ベースビューの場合はリクエストのメソッドに応じて動作を切り替えるような処理を実装する必要があって若干ややこしいですが、クラスベースビューの場合はこのあたりの処理の実装が不要となり、クラスベースビューに置き換えることで実装が簡潔になります。なので、ListViewDetailView 等に比べてクラスベースビューに置き換えるメリットも大きいと思います。

こういったインスタンスのデータベースへの新規登録に関してもウェブアプリで実装する機会が多い機能となると思いますので、CreateView も使いこなせるようになっておきましょう!

このサイトでは他の View のサブクラス についても説明していますので、他のページも是非読んでみてください!

DjangoのListViewの解説ページアイキャッチ 【Django】ListViewの使い方(クラスベースビューでの一覧リストページの実現) DjangoのDetailViewの解説ページアイキャッチ 【Django】DetailViewの使い方(クラスベースビューでの詳細ページの実現) UpdateViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】UpdateViewの使い方(クラスベースビューでのレコード更新ページの実現) DeleteViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】DeleteViewの使い方(クラスベースビューでのレコード削除ページの実現) FormViewでのクラスベースビューの実現方法解説ページアイキャッチ 【Django】FormViewの使い方(クラスベースビューで汎用的なフォームを扱う) LoginViewの使い方の解説ページアイキャッチ 【Django】LoginViewの使い方(クラスベースビューでのログインの実現) LogoutViewの使い方の解説ページアイキャッチ 【Django】LogoutViewの使い方(クラスベースビューでのログアウトの実現)

同じカテゴリのページ一覧を表示