初めに
複数の同じformを入力する際にformsetを使用し実装を行うときにいくつかハマったので
メモを残す。
例:
以下のコードが今回formsetを使用するmodel, form, viewです。
内容は適当なので気にしないでください。
# model
class User(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(null=False, max_length=100)
...
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# form
class UserForm(forms.ModelForm):
use_required_attribute = False
name = forms.CharField(...)
...
class Meta:
model = User
fields = [ 'name', ...]
# view
class UserView(UpdateView):
template_name = 'user_setting.html'
model = User
form_class = UserForm
FormSet = modelformset_factory(User, form=UserForm, extra=0)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['formset'] = self.FormSet(queryset=User.objects.filter(...).all())
return context
def form_valide(self, f):
formset = self.FormSet(self.request.POST)
if formset.is_valid():
....
return redirect(self.get_cuccess_url())
HTMLはこんな感じ
...
<!-- 入力欄 --->
{% csrf_token %}
{% for field in formset %}
{{ field.name }}
<!-- 他の項目 -->
{% endfor %}
...
事象1: ManagementFormデータが見つからないか、改竄されています
ManagementFormデータってなんぞやという感じで色々探したら、以下のようにformset.management_form
を挿入すれば良いとの事
改善策:
<!-- 入力欄 --->
{% csrf_token %}
{{ formset.management_form }}
{% for field in formset %}
{{ field.name }}
<!-- 他の項目 -->
{% endfor %}
事象2:'id':['このフィールドは必須です。']
idってなにformの何処にも定義してないんだけどーって思いながらハマっていました。
よく見たらformsetに格納されているformにinstance設定されていないし。。。
改善策1:
{% csrf_token %}
{{ formset.management_form }}
{% for field in formset %}
<!-- instance情報が隠れているので追加する必要がある -->
{% for hidden in field.hidden_fields %}
{{ hidden }}
{% endfor %}
<!-- ここまで -->
{{ field.name }}
<!-- 他の項目 -->
{% endfor %}
改善策2:
{% csrf_token %}
{{ formset.management_form }}
{% for field in formset %}
<!-- instance情報が隠れているので追加する必要がある -->
{{ field.id }}
<!-- ここまで -->
{{ field.name }}
<!-- 他の項目 -->
{% endfor %}