コンストラクタとは
オブジェクトを作成する際に最初に呼ばれるメソッドで、初期設定を行うメソッド。
formにコンストラクタを作成し、viewsにformに渡すためのinitメソッドで初期設定を行い、コンストラクタに引数を渡したいときはdef get_form_kwargs(self)のメソッドを作成して、渡したい値を設定する必要がある。
以下にも記載
モデルフォーム(modelForm)の初期設定でセレクトボタンの未選択モデル
※ポイント(重要)
viewsのdef get_form_kwargs(self)メソッドで指定した引数をformの`__init__`メソッドで受け取るのはDjangoの決まり。
Djangoのクラスベースビュー(特に`FormView`やそれを継承するビュー)では、`get_form_kwargs`メソッドがフォームインスタンスを生成する際に、フォームコンストラクタ(`__init__`メソッド)へ渡すキーワード引数(kwargs)を返します。
このメソッドをオーバーライドすることで、veiwsからformへ追加のデータを渡すことができます。
具体的には、`get_form_kwargs`メソッド内で辞書型の`kwargs`に追加のキーと値を設定し、これを`return`すると、これらのキーと値がフォームの`__init__`メソッドの引数として渡されます。
フォーム側では、`__init__`メソッドの引数リストにこれらのキーを明示的に追加するか、`**kwargs`を使ってキーワード引数を受け取り、必要に応じて処理します。
コンストラクタの作成の流れ
viewでinitのメソッドとinitに引数を渡すkwargsメソッドを作成する必要がある
実務の内容
適用開始日が選択されていたらセレクトボックスにコースを作成するというもの
バリエーションチェックを行うためにGETだけでなくPOSTの処理もkwrgsに記述する必要があった。
※コンストラクタでバリエーションチェックを通過するためにはviewのフォームメソッドに渡すクラスのget_form_kwargsにPOST時の処理を書く必要がある
なぜならバリエーションチェックはPOST時にチェックを行うため
実際のコード
view
def get_form_kwargs(self):
"""forms.pyのkwargsにキーワリューをセットする.
フォームの選択肢を、ユーザが参照可能なデータに絞るため
フォームに members_list を渡す
"""
kwargs = super().get_form_kwargs()
# POSTリクエストの場合、POSTから値を取得
if self.request.method == 'POST':
kwargs['update_items'] = self.request.POST.get('update_items')
kwargs['begin_date'] = self.request.POST.get('update_begin_date', None)
kwargs['end_date'] = self.request.POST.get('update_end_date', None)
else:
# GETリクエストの場合、GETから値を取得
kwargs['begin_date'] = self.request.GET.get('update_begin_date', None)
kwargs['end_date'] = self.request.GET.get('update_end_date', None)
kwargs['user'] = self.request.user
return kwargs
※ポイント(重要)
viewsからformに引数を渡すときは
kwargs[引数名] = self.request.GET(orPOST)("name属性で指定されている名前")
と書くことで、リクエストを通ってきたGETだったらURLのクエリ、POSTだったからbodyのクエリからそれぞれ値を取得することができる
formのコンストラクタメソッド
def __init__(self, *args, begin_date=None, end_date=None, update_items=None, user=None, **kwargs):
"""ScheduleListFormクラスの初期処理."""
super().__init__(*args, **kwargs)
if update_items:
self.fields['update_items'].initial = update_items
if user:
self.fields['update_facility'].queryset = \
user.pop_all_facilities()
facilities = user.pop_all_facilities()
facility_choice_list = []
for s_facility in facilities:
facility_choice_list.append((s_facility.id, s_facility.name))
facility_choices = tuple(facility_choice_list)
self.fields['search_facilities'].choices = facility_choices
current_courses = Course.get_current_courses()
self.fields['search_course'].queryset = current_courses
if begin_date:
select_course = Course.get_courses(begin_date, end_date)
self.fields['update_course'].queryset = select_course
※ポイント(重要)
①begin_date=None, end_date=None
フォームをインスタンス化する際にこれらの引数が提供されなかった場合、それぞれのデフォルト値として`None`が使用されることを意味します。
コンストラクタ(`__init__`メソッド)の引数にデフォルト値を設定することで、その引数をオプショナル(任意)にすることができます。
このテクニックは、特定の状況でのみ必要となる引数や、引数の値が動的に決まる場合に役立ちます。
②
kwargs`を引数リストの最後に含めることで、上記の指定された引数以外にも、任意の追加のキーワード引数を受け取ることができます。
これにより、フォームクラスが未来の拡張やカスタマイズに対しても柔軟に対応できるようになります。
クラスの定義について
今回、フォームの定義が特殊で今日の日付と一致するかというバリデーションチェックをしていたので、formファイルでのコースの定義を作り直している
class CourseModelChoiceField(forms.ModelChoiceField):
def to_python(self, value):
if not value:
return None
try:
return Course.objects.get(pk=value)
except Course.DoesNotExist:
raise ValidationError(self.error_messages['invalid_choice'], code='invalid_choice')
forms.ModelChoiceFieldを親クラスとしてオーバーライドを行う。
バリエーションチェックの部分だけモデルにコースが存在すれば、通過するようにしている。
定義を変えたので下記のコースの部分もCourseModelChoiceFieldから呼ばれるように修正した
update_course = CourseModelChoiceField(
label='コース',
required=False,
queryset=Course.objects.none(),
help_text='適用開始日以降のコースが選択可能です。'
)
※ポイント
update_courseはCouserModelChoiceFieldを使って()の中を定義するという意味