0

ネストされたフォームセットとして提示したい 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)のみが入力されている場合、保存要求は無視されます。このアプローチでまだ問題に遭遇したことはありませんが、新しいフォームを追加しようとはしていません。

4

1 に答える 1

1

同様の状況でフォームセットを使用extraしたとき、モデルのすべての必須フィールドを HiddenInputs としてフォームに含める必要がありました。少し醜いですが、誰かがハックアラウンドを持っているかどうか興味があります。

編集
上で書いたときは混乱しました.withを使用extrainitialて追加のフォームを事前入力するフォームセットに取り組んでいただけで、質問の詳細をすべて把握していませんでした.

私が正しく理解している場合、それらのそれぞれのLineFormSetsをインスタンス化する場所add_fieldsは同じResultインスタンスを指しますか?

この場合、問題があるため、実際には提供resultしたくありません。代わりに、そのフィールドを LineForm モデルフォームから完全に削除し、クラスを次のようinitialにカスタマイズできます。LineFormSet

class LineFormSet(forms.BaseModelFormSet):
    # whatever other code you have in it already
    # ...
    # ...
    def __init__(self, result, *args, **kwargs):
        super(LineFormSet, self).__init__(*args, **kwargs)
        self.result = result

    def save_new(self, form, commit=True):
        instance = form.save(commit=False)
        instance.result = self.result
        if commit:
            instance.save()
        return instance

    def save_existing(self, form, instance, commit=True):
        return self.save_new(form, commit)

(これは Django 1.3 および 1.4 では問題ないはずですが、他のバージョンは不明です)

add_fieldsしたがって、メソッドの関連部分は次のようになります。

   form.nested = [
        LineFormSet(
            result = instance,
            queryset = q, #data=self.data, instance = instance, prefix = 'LINES_%s' % pk_value)]
            prefix = 'lines-%s' % pk_value,
        )]
于 2012-06-08T19:09:35.373 に答える