class FormMixin(ContextMixin):
def get_form(self, form_class=None):
"""Return an instance of the form to be used in this view."""
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs())
def get_form_kwargs(self):
"""Return the keyword arguments for instantiating the form."""
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
def get_form(self, form_class=None):
"""Return an instance of the form to be used in this view."""
if form_class is None:
form_class = self.get_form_class()
return form_class(**self.get_form_kwargs())
def get_form_kwargs(self):
"""Return the keyword arguments for instantiating the form."""
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
class ModelFormMixin(FormMixin, SingleObjectMixin):
def get_form_kwargs(self):
"""Return the keyword arguments for instantiating the form."""
kwargs = super().get_form_kwargs()
if hasattr(self, "object"):
kwargs.update({"instance": self.object})
return kwargs
藤本 博子さんの投稿
(投稿ID: 5454)
forms.Formクラスを継承したフォームクラスをクラスベースドビューで呼び出す時のことについて確認したいことがあります。
お手数をおかけしますが、どうぞよろしくお願いいたします。
forms.Formクラスを継承したフォームクラスを、generic.FormViewクラスまたはgeneric.Viewクラスを継承したビューで取得し、かつモデルフィールドの内容を変更(更新)することができました。
POST実行時の処理
1.cleanded_dataの辞書型データを受取とる
2.モデルフィールドのオブジェクトをinstanceという変数に格納。
3.モデルフィールドの属性をcleaned_dataの値で適宜セットして、instance.save()でオーバーライド
4.return super().form_valid(form)すると、モデルフィールドの上書きができた。
しかし,forms.Formクラスをgeneric.Updateviewを継承したビューで取得しようとすると、BaseForm.__init__() got an unexpected keyword argument 'instance'
BaseForm.__init__() が予期せぬキーワード引数 'instance' を受け取りましたとエラーが返ります。
私のコードの問題なのか、UpdateViewとforms.Formクラスを継承したフォームクラスの組み合わせはもともと出来ないのか。generic.UpdateViewとgeneric.FormViewの違いをedit.pyのコードをみて自分なりに考えたのですが、理解の誤りがあるか確認したいと思いました。
次回のゼミの時にでもご教示いただけるとありがたいです。
※結論としては、以下の2パターンで使い分けるのかと思います。
・モデルフィールドに関連しない処理のフォームの場合
forms.Formクラスを継承したフォームクラスを使用する。
クラスベースドビューで呼び出す場合は、generic.FormViewやgeneric.Veiw、テンプレートファイルを使ったページの表示の場合は、generic.TemplateVeiwを使う。
・モデルフィールドに反映するフォームを作成する場合
forms.ModelFormクラスを継承したフォームクラスを使う。forms.ModelFormはclass Meta内のmodelやフォームフィールドを自動で作成してくれる。
クラスベースドビューは、用途に合わせて、generic.UpdateView,DeleteView,CreateViewを使用する。
※generic.FormViewクラスが継承するclass FormMixin(ContextMixin):の処理のコードを参照しました。
get_formメソッドと戻り値を返すget_form_kwargsメソッド
POSTメソッドの場合は、form=sef.get_form_class()としてフォームクラスのインスタンスを取得して、キーワード引数にdata=request.POST、ファイルの時は、FILESを渡すと理解しました。
FormViewが継承するFormMixinクラス edit.py
※generic.UpdateViewの場合
POST時に、BaseUpdateViewクラスのget_objectメソッドでモデルインスタンスをself.objectに格納するようです。
UpdateViewが継承するBaseUpdateView edit.py
そして、私がform.Formクラスと関連づけたため、formViewクラスが継承するclass FormMixinクラスのget_form_kwargsメソッドが呼び出された。
メソッドを上書きしようとしたが、引数は以下の通り"data": self.request.POST,ファイルがある場合は、"files": self.request.FILES。
self.objectのモデルインスタンスを受取ってしまうと、予期せぬキーワード引数を受取りました、というエラーがかえってきたのか?と思いました。
formViewクラスが継承するclass FormMixinクラスのget_form_kwargs関数は、POSTメソッドの返り値を引数に取って、get_form関数に返す。
ここでエラーになった。
edit.py 39行目 エラーが発生した箇所です。
※UpdateViewが継承しているModelFormMixinのget_form_kwargsメソッド
属性がobjectの場合は "instance":self.objectの辞書をkwargsに代入するようになってます。
それゆえ、form.ModelFormクラスの場合はself.objectを引数にとれるし、モデルフィールドに反映する処理ができるのかと思いました。
理解不足のままの質問となり申し訳ございません。
次回のゼミの時にでも解説いただけるとありがたいです。
どうぞよろしくお願いいたします。
小川 慶一さんのコメント
(コメントID: 8092)
generic.Updateview を継承してご自身で作られたビューをすべて見せてください。
藤本 博子さんのコメント
(コメントID: 8093)
ご返事ありがとうございます。
お手数をおかけしますが、どうぞよろしくお願いいたします。
・以下のUpdateViewクラスを継承したviewのコードの中で、
form_classをforms.Formクラスを継承したclass EditByFormsForm(forms.Form)を定義するとエラーになりました。
forms.ModelFormを継承したclass EditForm(forms.ModelForm):だと更新できます。
URLは、http://127.0.0.1:8000/edit/105/ (pkが105)です。
path('edit/<int:pk>/', PictureUpdateView.as_view(), name='picture_edit'),
・form_classをforms.Formクラスを継承したclass EditByFormsForm(forms.Form)は、generic.FormViewクラスを継承した
class EditViewByForm(FormView):だと更新できました。
URLは、http://127.0.0.1:8000/edit_form/105/ (pkが105)です。
path('edit_form/<int:pk>/', EditViewByForm.as_view(), name='picture_edit_form'),
ご参考まで、githubのリンクを記載します。
https://github.com/puji8493/upload_pictures.git
generic.UpdateVeiwを継承したView
class EditByFormsForm(forms.Form)を定義するとエラーになります。
forms.ModelFormを継承したclass EditForm(forms.ModelForm):だと更新できました。
FormViewクラスとclass EditByFormsForm(forms.Form)と組み合わると、更新ができます。
UpdateViewではないのですが、念のため掲載いたします。
お手数をおかけしますが、どうぞよろしくお願いいたします。
小川 慶一さんのコメント
(コメントID: 8094) 添付ファイルのダウンロード権限がありません
BaseForm.__init__() got an unexpected keyword argument 'instance'
書かれているとおり「BaseForm.__init__() が予期せぬキーワード引数 'instance' を受け取りました」ということです。
forms.ModelForm クラスの __init__ メソッドは、 instance 引数を取り得ます。
なので、これを使った場合は同様のエラーは生じませんでした。
一方、forms.Form クラスの __init__ メソッドは、 instance 引数を取りません。
なので、冒頭に示したエラーが出ました。
そもそも、 UpdateView はモデルを更新するための View です。
なので、UpdateView の form_class 属性の値としては、 forms.Form クラスを継承したクラスではなく、 forms.ModelForm クラスを継承したクラスを指定します。
そういう前提で作りこまれた View なので、 form_class で指定されたクラスのインスタンス化の際に、 instance 引数を渡します。
参考までに書くと、上記「form_class で指定されたクラスのインスタンス化の際に、 instance 引数を渡す」処理をしているのは、 UpdateView の get_form メソッドです。
このメソッドは、以下の処理をしています。
[1]
self.get_form_class クラスを呼び出す。
その中で、クラス変数 form_class の値を取得する。
[2]
self.get_form_kwargs メソッドを呼び出す。
その中で、まず、FormMixin の self.get_form_kwargs を実行する。
これにより、 initial, prefix, data, files を引数として準備する。
さらに、ModelFormMixin の self.get_form_kwargs を実行する。
これにより、 instance=self.object というキーワード引数を準備する。
[3]
[1] で得た form_class について、以下によりインスタンス化を行う。
form_class(**self.get_form_kwargs())
この [1]-[3] の過程を経て、 get_form メソッドは、 EditForm(instance=self.object) を行うわけです。([2]で記載したとおり、他にもキーワード引数を含む場合もある)
参考: CCBV UpdateView
https://ccbv.co.uk/projects/Django/4.1/django.views.generic.edit/UpdateView/
FormView は ModelFormMixin を継承していないので、上記 [2] で instance=self.object というキーワード引数を準備しません。
なので、 forms.Form クラスのフォームでも動作します。
参考: CCBV FormView
https://ccbv.co.uk/projects/Django/4.1/django.views.generic.edit/FormView/
…と、まずは、この説明でどうでしょうか。
CCBV の UpdateView についての解説をキャプチャしたものを添付しておきました。
藤本 博子さんのコメント
(コメントID: 8095)
分かりやすく解説くださいまして、ありがとうございます。
FormクラスとModelFormの違いが整理できました。
クラスベースドビュー、モデルフォームとも実装はしていましたが、短いコードで書ける分、そのプロセス、内部処理が見えず混乱してしまいました。
小川先生の解説をなぞらえながらコードを読み、内部の処理の過程、継承のイメージができました。
ありがとうございます。
すっきりしたので、自分のつくったアプリの機能を追加するなどしました。
画像登録アプリ:他テーブルと紐づけするモデル(コメント機能)を作成して、該当する画像にコメントを複数登録、詳細ページでコメントを一覧表示する。
英単語登録アプリ:単語に関連する画像を登録するモデルフィールドを追加、登録・表示する機能を追加しました。画像は一覧ページ、詳細ページにも表示するようにしました。
自分が作りたいと思っていたものができ、とてもうれしいです。
ご教示くださいまして、ありがとうございました。