formsetを使います。
ネット上では、forms.pyの中でフォームセットを作っているパターンの紹介が多いですが、今回はviews.pyの中でやります。
汎用性があるものは、forms.pyにまとめたり、クラス化したりするのが良いかと思います。
まず、forms.pyに普通のフォームを書きます。ModelFormを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from django import forms from .models import MyModel class MyForm(forms.ModelForm): class Meta: model = MyModel fields = ( 'field_1', 'field_2', 'field_3', ) widgets = { 'field_1': forms.Select(), 'field_2':forms.Select(), 'field_3':forms.NumberInput(), } def __init__(self, *args, **kwargs): # Classの指定など for field in self.fields.values(): field.widget.attrs['class'] = 'form-control' |
views.pyの中で、フォームセットを使いテンプレートにレンダリングします。処理が複雑になるので、クラスviewは使わず、関数viewで書きます。
保存時に一括でsave()がうまくいかない時は、transactionを使用し、フォームセットのPOST値をループで回してsave()するとできます。(本当はbulk_createを使いたかったのですがうまくいきませんでした。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
from django.forms.models import modelformset_factory from django.db import transaction def HogeCreateView(request): template_name = 'myapp/hoge_form.html' # フォームセット定義 # modelformset_factoryを使う。 # modelformじゃない時はforms.formset_factory MyFormSet= modelformset_factory( model=MyModel, form=MyForm, extra=3, # セットの表示数 defaultは1 max_num=3 # 最大表示数 defaultは1 ) if request.method == 'GET' : # フォームの初期値を指定する場合 form_initial = [{ 'field_1' : 'initial_value_1', 'field_2' : 'initial_value_2', }] # フォームセットのオブジェクト生成 form_set = MyFormSet( initial=form_initial, # 新規作成フォームのみ表示(既存レコードは表示しない) queryset=MyModel.objects.none() ) else : # POST form_set = MyFormSet(request.POST) if form_set.is_valid() : posts = form_set.save(commit=False) # 保存処理 with transaction.atomic() : # 各々save(m2mってのもあるらしいが試してない) for p in posts : # 他のフォームを併設している場合、そこでsaveしたレコードのPKを使う場合はobject.pkでとれる #p.parent_pk = other_post.pk p.save() messages.info(request, f'保存しました。') return redirect('myapp:top') # レンダリング context = { 'form_set': form_set, } return render(request, template_name, context) |
テンプレートは、ただフォームセットを埋め込むだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
{{ form.certifications.errors }} <div class="container"> <div class="row"> <form method="post"> {% csrf_token %} <div class="clearfix"> {{ form_set }} </div> <div> <input type="submit" class="btn" value="登録" /> <a class="btn btn-secondary" href="{% url 'myapp:top' %}">戻る</a> </div> </form> </div> </div> |
参考サイト)
- https://bugsdb.com/_en/debug/0ea081ae2d1457cc7957f1dfeececfb9
- https://qiita.com/sand/items/9b9d5d0b3a189a05711f
- https://blog.narito.ninja/detail/30/#_9
- https://stackoverflow.com/questions/4271686/object-has-no-attribute-save-django
- https://stackoverflow.com/questions/16040493/save-a-formset-of-custom-forms-django/16054812
- https://qiita.com/qtatsunishiura/items/a6cc11e025aca1c16ed1
- https://teratail.com/questions/138985
- https://www.nblog09.com/w/2019/05/18/django-form-initial/