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

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

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

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

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

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

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

CreateView

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

レコードの新規登録を実現するクラス

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

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

CreateViewのimport
from django.views.generic import CreateView

ウェブアプリでは、データベースへのレコードの新規登録を行うものが非常に多いです。例えば、ユーザーの新規登録も、コメントの新規投稿も、レコードの新規登録によって実現されます。そして、このようなレコードの新規登録を実現するビューは、CreateView を継承することで簡単に作成することが可能です。

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

スポンサーリンク

GETPOST の両方のメソッドに対応するクラス

こういった、レコードの新規登録を行うような場合、フォームを表示してユーザーからの必要な情報の入力を受け付け、さらに、そのフォームから送信されてきたデータに応じたレコードをテーブルに新規登録するようなビューを作る必要があります。

レコードの新規登録を行う上で必要となるビューの機能を説明する図

CreateView においては、メソッドが GET のリクエストを受け取った時に CreateViewget メソッドが実行され、その get メソッドでフォームを表示する処理が実行されるようになっています。

メソッドがGETのリクエストを受け取った時にCreateViewのgetメソッドが実行される様子

また、メソッドが POST のリクエストを受け取った時には CreateViewpost メソッドが実行され、その post メソッドでフォームから送信されてきたデータに応じたレコードをテーブルへ新規登録する処理が実行されるようになっています。そして、レコードの新規登録後には特定の URL へのリダイレクトレスポンスが返却されるようになっています。(フォームから送信されてきたデータが妥当でない場合には、再度フォームを表示するような処理が実行されるようにもなっています)。

メソッドがPOSTのリクエストを受け取った時にCreateViewのpostメソッドが実行される様子

CreateView を継承するクラスを定義すれば、そのクラスには上記のようなメソッドも引き継がれることになります。なので、CreateView を継承するクラスを定義することで、上記のような処理を別途実装することなく、データベースのテーブルへのレコードの新規登録を実現するビューが作成することができることになります。

また、上記のように、メソッドが GET のリクエストと POST のリクエストの両方に対応しているという点は、CreateView の特徴の1つとなります。

例えば View のサブクラス には下記ページで紹介している ListViewDetailView がありますが、これらはデータベースのレコードを表示することに特化したクラスであり、メソッドが GET のリクエストにのみ対応しています。そのため、これらの ListViewDetailView は post メソッドを持っていません。

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

CreateView を継承するメリット

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

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

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

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

それに対し、クラスベースビューの場合、メソッドが GET であるか POST であるかの判断を行い、それに応じて実行する処理を切り替えるような実装が View のサブクラス で既に行われています。そして、その View のサブクラス の例の1つが、今回扱う CreateView となります。したがって、開発者は、関数ベースビューでは必要だったメソッドに応じた処理の切り替えの実装が不要となります。

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

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

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

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

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

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

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

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

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

これらの get メソッドや  post メソッドの動作を変化させ、開発するウェブアプリに適したビューに仕立てるために、CreateView を継承するクラスでのクラス変数やメソッドの定義が必要となります。

スポンサーリンク

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

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

CreateView にはフォームが必要

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

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

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

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

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

CreateView が利用するフォームは『モデルフォーム』

ここでポイントになるのがフォームクラスのインスタンスの 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 で指定可能

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

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

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

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

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

CreateView が利用するフォームは model & fields で指定可能

後者に関しては、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 のオーバーライドにより実現することができます。

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

ただ、上記で紹介したクラス変数やメソッド以外のものも 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

CreateView のサブクラスにおいては、この form_class、もしくは、後述で紹介する model & fields の定義が必須となります。 

スポンサーリンク

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 を定義した場合、テンプレートファイルは forum/templates/forum/create.html に用意しておく必要があります。

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'

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

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

template_name を指定しない場合は、クラス変数 model の定義が必須となるという点に注意してください。template_name を指定しない場合は、前述のとおり アプリ名/モデルクラス名_form.html がテンプレートファイルのパスとなるのですが、この モデルクラス名 部分は、クラス変数 model に指定したモデルクラスのクラス名を小文字に変換したものとなります。したがって、クラス変数 template_name とクラス変数 model の両方を定義していない場合、テンプレートファイルのパスが定まらなくなり、下記のような例外が発生することになります。

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

このような例外が発生したら、クラス変数 model or クラス変数 template_name を定義して例外を解消するようにしてください。

スポンサーリンク

template_name_suffix

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

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 となります。

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_context_data メソッドから呼び出しされる 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 の場合の処理を変更する で説明した通りで、フォームから送信されてきたデータの妥当性の検証結果が OK である場合に実行されるメソッドになります。そして、このメソッド内部ではフォームから送信されてきたデータをレコードとしてテーブルに保存し(新規登録)、さらに特定の URL にリダイレクトするためのレスポンスを返却する処理が行われるようになっています。そのため、これ以外の処理を form_valid で実施するようにしたいのであれば、form_valid のオーバーライドが必要になります。

例えば下記は、【Django入門14】クラスベースビューの基本掲示板アプリのビューをクラスベースに変更する で紹介した form_valid のオーバーライドの例になります。下記の通り、form_valid は第1引数を self、第2引数を form として定義する必要があります。

下記のように 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 のオーバーライドによって実現可能であることは是非覚えておいてください。

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

また、フォームから送信されてきたデータの妥当性の検証結果が 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_absolute_url メソッドがモデルクラスに定義されていない場合は下記の例外が発生することになります。なので get_success_url メソッドをそのまま利用する場合は、クラス変数 success_url を定義する or モデルクラス側で get_absolute_url メソッドを定義することが必須となります。

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

リダイレクト先の URL を設定する でも解説した通り、静的に決まる URL であれば、クラス変数を定義し、動的に決める必要のある URL であれば、メソッドを定義するので良いと思います。モデルクラス側に get_absolute_url を定義して動的にリダイレクト先の URL 返却する例に関しては、後述の CreateView の利用例 で紹介します。

もしくは、get_success_url メソッドをオーバーライドしてしまって上記のような作りから根本的に変更してしまうという手もあります。

例えば下記は、【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 メソッドでは、クラス変数 success_url も参照していませんし、get_absolute_url メソッドも呼び出していないため、両方が定義されていなくても例外が発生しません。また、get_success_url メソッドでは、self.object から事前にテーブルに新規登録されたインスタンスを取得することができます。なので、get_success_url メソッドの中で self.object を参照することで、テーブルに新規登録されたインスタンスに応じて動的に生成した URL を返却するようなことが可能となります。

ちなみに、上記の get_success_url では、urls.py で 'user' と名付けられた URL の後ろに self.object.pk (新規登録されたインスタンスのプライマリーキー) が追加された URL が返却されることになります。

CreateView を継承するクラスを利用する上では、レコードの新規登録後のリダイレクト先の URL も重要であり、この URL の決め方には下記の3つが存在することは覚えておきましょう!

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

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

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

CreateView では、コンテキストは GET メソッドのリクエストを受け取って get メソッドが実行された時、もしくは、POST メソッドのリクエストを受け取って post メソッドが実行されたけど妥当性の検証結果が NG であった時に生成されます(フォームを再表示するため)。

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

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

このようなコンテキストを利用したテンプレートファイルからのフォームの表示の仕方の詳細については下記ページの フォームの表示 の節を参照していただければと思います。

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

また、他のデータをテンプレートファイルから参照したい場合は、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 メソッドの実行結果が返却されるようになっています。今回の例であれば、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 にリダイレクトされます( つまり /forum/comment/プライマリーキー/ へリダイレクトされる)。それにより、次は 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の使い方(クラスベースビューでのログアウトの実現)

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