ネストされたフォームセットとして提示したい 3 レベルのテスト モデルがあります。各テストには複数の結果があり、各結果には複数の行を含めることができます。ネストされたフォームセットを作成するための Yergler の方法と、より最近の Django バージョン用に Yergler のコードを更新するこのSOの質問に従っています (私は 1.4 を使用しています)。
FormSet の "extra" パラメータを使用してフォームセットに余分な Line を含めたいので、問題が発生しています。各 Line の ForeignKey は Line が属する Result を指している必要がありますが、ユーザーが変更することはできないため、HiddenInput フィールドを使用して FormSet の各 Line に Result を含めます。
result
フィールドは常に (add_fields で) 入力されますが、text
(severity
ユーザーが別の行を入力しないことを選択した場合) は入力されない可能性があるため、これは「必須フィールドの欠落」検証エラーにつながります。この状況を処理する正しい方法がわかりません。add_fields に初期値を含める必要はなく、実際に機能するより良い方法があるに違いないと思います。result
この質問の下部に向かって以下を更新します
必要に応じて詳細を追加します。
私のカスタムフォームセットのコード:
LineFormSet = modelformset_factory(
Line,
form=LineForm,
formset=BaseLineFormSet,
extra=1)
class BaseResultFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(BaseResultFormSet, self).__init__(*args, **kwargs)
def is_valid(self):
result = super(BaseResultFormSet, self).is_valid()
for form in self.forms:
if hasattr(form, 'nested'):
for n in form.nested:
n.data = form.data
if form.is_bound:
n.is_bound = True
for nform in n:
nform.data = form.data
if form.is_bound:
nform.is_bound = True
# make sure each nested formset is valid as well
result = result and n.is_valid()
return result
def save_all(self, commit=True):
objects = self.save(commit=False)
if commit:
for o in objects:
o.save()
if not commit:
self.save_m2m()
for form in set(self.initial_forms + self.saved_forms):
for nested in form.nested:
nested.save(commit=commit)
def add_fields(self, form, index):
# Call super's first
super(BaseResultFormSet, self).add_fields(form, index)
try:
instance = self.get_queryset()[index]
pk_value = instance.pk
except IndexError:
instance=None
pk_value = hash(form.prefix)
q = Line.objects.filter(result=pk_value)
form.nested = [
LineFormSet(
queryset = q, #data=self.data, instance = instance, prefix = 'LINES_%s' % pk_value)]
prefix = 'lines-%s' % pk_value,
initial = [
{'result': instance,}
]
)]
テストモデル
class Test(models.Model):
id = models.AutoField(primary_key=True, blank=False, null=False)
attempt = models.ForeignKey(Attempt, blank=False, null=False)
alarm = models.ForeignKey(Alarm, blank=False, null=False)
trigger = models.CharField(max_length=64)
tested = models.BooleanField(blank=False, default=True)
結果モデル
class Result(models.Model):
id = models.AutoField(primary_key=True)
test = models.ForeignKey(Test)
location = models.CharField(max_length=16, choices=locations)
was_audible = models.CharField('Audible?', max_length=8, choices=audible, default=None, blank=True)
ラインモデル
class Line(models.Model):
id = models.AutoField(primary_key=True)
result = models.ForeignKey(Result, blank=False, null=False)
text = models.CharField(max_length=64)
severity = models.CharField(max_length=4, choices=severities, default=None)
アップデート
昨夜、これを LineForm(ModelForm) クラスに追加しました:
def save(self, commit=True):
saved_instance = None
if not(len(self.changed_data) == 1 and 'result' in self.changed_data):
saved_instance = super(LineForm, self).save(commit=commit)
return saved_instance
結果 (HiddenInput)のみが入力されている場合、保存要求は無視されます。このアプローチでまだ問題に遭遇したことはありませんが、新しいフォームを追加しようとはしていません。