フォーム機能
したこと:リレーション
ユーザー入力を管理する
- フォームを定義
- データベースにデータを保存
- モデルと紐づけ
- データのバリテーション(データの型が正しいかどうか)
流れ
forms.py → scripts.py → views.py → urls.py → html
役割
フォームの定義 -> 選択肢 -> 見た目 -> urlの設定 -> html(ユーザーの見た目)
forms.py
- リレーションのフォームを定義
- class metaでフォームで使うmodel, field, widgetを指定。
- defでフォームの初期化
- 編集の際はリレーションの情報を取得
#bookフォーム
class BookForm(forms.ModelForm):
# 分類のチェックボックス
types = forms.ModelMultipleChoiceField( #formsのデータベースモデルから複数選択できる
queryset=BookType.objects.all(), #選択肢のリスト = BookTypeからすべてのデータ取得
widget=forms.CheckboxSelectMultiple(attrs={'class': 'form-check-input'}), #見た目 = attrs(htmlの属性)はチェックボックス
label='分類', # タイトル「分類」に変更
required = False, #空欄のままでもOK
)
# ジャンルのチェックボックス
genres = forms.ModelMultipleChoiceField(
queryset=BookGenre.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs={'class': 'form-check-input'}),
label='ジャンル', # 修正: 本来のタイトルへ
required=False,
)
# 状態のチェックボックス
statuses = forms.ModelMultipleChoiceField(
queryset=BookStatus.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs={'class': 'form-check-input'}),
label='状態', # 修正: 本来のタイトルへ
required = False,
)
# 評価のチェックボックス
values = forms.ModelMultipleChoiceField(
queryset=Value.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs={'class': 'form-check-input'}),
label='評価', # 修正: 本来のタイトルへ
required=False,
)
class Meta:
model = Book
fields = ['title', 'author', 'designer', 'types', 'genres', 'series', 'statuses', 'values']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'タイトルを入力してください'}),
'author': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '著者名を入力してください'}),
'designer': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '絵師を入力してください'}),
'series': forms.NumberInput(attrs={'class': 'form-control', 'placeholder': '巻数を入力してください'}),
}
def __init__(self, *args, **kwargs):
request = kwargs.pop('request', None)
super().__init__(*args, **kwargs)
if request and request.path == '/book/search/':
self.fields.pop('series')
if self.instance and self.instance.pk: # 編集モードの場合
# ManyToManyFieldの初期値を設定
self.fields['types'].initial = self.instance.type.all()
self.fields['genres'].initial = self.instance.genre.all()
self.fields['statuses'].initial = self.instance.status.all()
self.fields['values'].initial = self.instance.value.all()
scripts.py
- for in []で選択肢を作る
- 保存:model名.objects.get_or_create(name=name)
#Valueのデータ登録
for name in ['SS', 'S', 'A', 'B', 'C', 'D']:
Value.objects.get_or_create(name=name)
#BookTypeのデータ登録
for name in ['小説', 'ライトノベル', '漫画', 'ビジネス本', 'お金']:
BookType.objects.get_or_create(name=name)
#BookGenreのデータ登録
for name in ['推理・ミステリー', '青春', '恋愛', 'SF(サイエンスフィクション)', 'ファンタジー',
'ホラー', '経済', '政治', '歴史・時代', 'ミリタリー', '児童', '官能']:
BookGenre.objects.get_or_create(name=name)
#BookStatusのデータ登録
for name in ['未読', '読書中', '読了', '購入済み', '気になる']:
BookStatus.objects.get_or_create(name=name)
views.py
- 選択肢1:int:pk等の場合、pkをコードで受け取っている場合勝手にurls.pyで解析してくれる
- 選択肢2:新規作成
- リクエストがある場合:保存 -> book_list(一覧ページに飛ぶ)
- リクエストがない場合:fieldの情報を追加
- render(book_formのページに情報を渡す)
def book_form(request, pk=None):
if pk: # pkが指定されている場合、編集モード
book = get_object_or_404(Book, pk=pk) #urls.pyでpkを解析してくれる
form = BookForm(instance=book)
else: # pkがない場合、新規作成モード
book = None
form = BookForm()
if request.method == 'POST': # POSTリクエストの場合、フォームを保存
form = BookForm(request.POST, instance=book) #フォームにPOSTデータを関連づけ(結びつけ)
if form.is_valid():
print(form.cleaned_data)
book = form.save(commit=False) #インスタンス作成
book.save() #データベースに保存
form.save_m2m() #manytomanyfiledの関連データを保存 (今回はできなかった)
# ManyToManyFieldに手動で関連付けを追加
book.type.set(form.cleaned_data.get('types', []))
book.genre.set(form.cleaned_data.get('genres', []))
book.status.set(form.cleaned_data.get('statuses', []))
book.value.set(form.cleaned_data.get('values', []))
return HttpResponseRedirect(reverse('book_list')) # リダイレクト
# 各フィールドの情報を追加
field_data = []
for field in form:
field_data.append({
'field': field,
'is_checkbox': isinstance(field.field.widget, forms.CheckboxSelectMultiple) # チェックボックス判定(ラジオボタンやドロップダウンリストにも必要)
})
# テンプレートに渡す
return render(request, 'myapp/book_form.html', {'field_data': field_data})
urls.py
- 新しいページ
- 編集ページ
path('book/form/', views.book_form, name='book_form'),
path('book/form/int:pk/', views.book_form, name='book_edit'),