2012-06-08 48 views
0

我有一個3級的測試模型,我想將其作爲嵌套表單集呈現。每個測試都有多個結果,每個結果可以有多個行。我下面Yergler's method創建嵌套表單集,與this SO question,更新Yergler對最近的Django的版本(我是1.4)在Django中保存嵌套表單的正確方法

我遇到了麻煩,因爲我想用表單集的「額外」參數包括代碼一起表單集中的額外行。每行的ForeignKey必須指向該行所屬的結果,但不能由用戶更改,因此我使用HiddenInput字段在每個FormSet的行中包含結果。

這導致了「丟失必填字段」驗證錯誤,因爲result場總是充滿了(在add_fields,傳入三個),但textseverity不得(如果用戶選擇不進入另一條線)。我不知道處理這種情況的正確方法。我想認爲,我不需要在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)填充出。我還沒有遇到任何問題,但我還沒有嘗試添加新的表單。

回答

1

當我在相似的情況下在窗體集上使用extra時,我最終不得不在表單中包含模型中的所有必填字段,如HiddenInputs。有點醜陋,但它的工作,好奇,如果任何人有一個黑客。

編輯
我很困惑,當我寫了上面,我只是一直在使用的formsets與extrainitial預先填充額外的形式工作,我也沒有完全得到了你的問題,所有的細節。

如果我理解正確,那麼你實例化LineFormSet s在add_fields中的每一個會指向相同的Result實例嗎?

在這種情況下,由於存在問題,您並不是真的想在initial中提供result。相反,你可以完全刪除從LineForm模型的形式,即場和定製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, 
     )] 
+0

您能否詳細解釋一下該方法的工作原理?我已經包含一個必填字段作爲HiddenInput,這是我的問題的來源(這是我的第一個Django項目也沒有幫助) – JohnWoltman

+0

我沒有仔細閱讀你的問題,它只是讓我想起了一些我我一直在做我自己的代碼。我修改了我的答案,希望更有幫助。 – Anentropic

+0

從Django角度看,這是有道理的,但是通過從模型代碼中刪除「result」字段將意味着Django不再將結果保存到正確的表中了。我認爲。剛剛接觸Django,我不太確定。 – JohnWoltman

相關問題