2012-05-18 71 views
3

this question我想將我的表格從常規Form轉換爲ModelForm,這樣我就可以利用ModelForm中的instance參數。如何以編程方式在Django的ModelForm中設置字段?

這是我目前的形式代碼:

class OrderDetailForm(forms.Form): 
    def __init__(
     self, 
     user, 
     can_edit_work_type=None, 
     can_edit_vendor=None, 
     can_edit_note=None, 
     *args, 
     **kwargs 
    ): 
     super(OrderDetailForm, self).__init__(*args, **kwargs) 

     if can_edit_work_type: 
      self.fields['work_type'] = forms.ChoiceField(choices=Order.WORK_TYPE_CHOICES) 
     if can_edit_vendor: 
      self.fields['vendor'] = forms.ModelChoiceField(
       queryset=Vendor.objects.all(), 
       empty_label="Choose a vendor", 
      ) 
     if can_edit_note: 
      self.fields['note'] = forms.CharField(widget=forms.Textarea) 

    def clean(self): 
     super(OrderDetailForm, self).clean() 

     if 'note' in self.cleaned_data: 
      if len(self.cleaned_data['note']) < 50: 
       self._errors['note'] = self.error_class([u"Please enter a longer note."]) 

       del self.cleaned_data['note'] 

     return self.cleaned_data 

正如你所看到的,我有一些if聲明,確定域是否甚至顯示你的表格(在邏輯上這意味着某些用戶只能修改某些部分的領域)。

我該怎麼做ModelForm?我明白fields是一個元組,因此它不能像我在Form中那樣追加。所以我想做點像

class OrderDetailForm(forms.ModelForm): 
    class Meta: 
     model = Order 
     # fields = ('work_type', 'vendor', 'note') I can't do that since I need to be able to control it. See below. 

     # Can I control widgets even if that field doesn't exist? 
     widgets = { 
      'note': forms.Textarea(), 
     } 

    def __init__(
     self, 
     user, 
     can_edit_work_type=None, 
     can_edit_vendor=None, 
     can_edit_note=None, 
     *args, 
     **kwargs 
    ): 
     super(OrderDetailForm, self).__init__(*args, **kwargs) 

     fields = [] 

     if can_edit_work_type: 
      fields.append('work_type') 
     if can_edit_vendor: 
      fields.append('vendor') 
     if can_edit_note: 
      fields.append('note') 

     self.Meta.fields = tuple(fields) # Does this work? 

    def clean(self): 
     super(OrderDetailForm, self).clean() 

     if 'note' in self.cleaned_data: 
      if len(self.cleaned_data['note']) < 50: 
       self._errors['note'] = self.error_class([u"Please enter a longer note."]) 

       del self.cleaned_data['note'] 

     return self.cleaned_data 

這可能嗎?如何控制ModelForm中的字段?

回答

5

另一種可能的方法是在排除基於所述請求字段中的視圖生成一個內嵌表格的類,例如,定義爲階模型正常模型形式,稱爲OrderDetailForm:

class OrderDetailForm(forms.ModelForm): 
    class Meta: 
     model = Order 
     fields = ('work_type', 'vendor', 'note') 
     widgets = { 
      'note': forms.Textarea(), 
     } 

在該視圖,例如,編輯訂單,創建一個基於OrderDetailForm的自定義表單:

def edit(request, order_id): 
    order = Order.objects.get(pk=order_id) 
    can_edit_work_type = bool(request.REQUEST.get('can_edit_work_type', False)) 
    can_edit_vender = bool(request.REQUEST.get('can_edit_vender', False)) 
    can_edit_note = bool(request.REQUEST.get('can_edit_note', False)) 

    exclude_fields = [] 

    if not can_edit_work_type: 
     exclude_fields.append('work_type') 

    if not can_edit_vender: 
     exclude_fields.append('vender') 

    if not can_edit_note: 
     exclude_fields.append('note') 

    class CustomizedOrderForm(OrderDetailForm): 
     class Meta: 
      model = Order 
      exclude = tuple(exclude_fields) 

    if request.method == 'POST': 
     form = CustomizedOrderForm(instance=order, data=request.POST) 
     if form.is_valid(): 
      form.save() 
    else: 
     form = CustomizedOrderForm(instance=order) 
    return render(request, 'order_form.html', {'form': form}) 
+0

我喜歡儘可能地保留我的請求處理程序,並將邏輯放在'forms','models'和其他地方,而不是處理程序本身。 –

+0

這不會在有效的帖子後返回HttpResponse。 –

+0

這不會將字段添加到表單。 ...或者這樣做......嗯......這有點兒很棒,但是處理程序中嵌套的類只是錯誤的;) –

1

ModelForm API與常規的Form非常相似。好處是你現在得到model validation,除了像默認小部件,實例kwarg和save方法之類的便利之外。

fieldsattr還是dict -like。您可以看到由元類here構建的字段。然後,去through,inheritance並且在BaseModelForm.__init__中呼叫super(),我們到達declared fields,originally,SortedDictdeepcopy。這是FormModelForm的共同點,兩個子類BaseForm

將字段放在exclude中,並按照原來的方式將其添加到__init__中。

以同樣的方式清潔它們。

然後,您可以覆蓋save方法:您可以撥打super()來獲取對象,並根據需要處理cleaned_data中的數據。

class OrderDetailForm(forms.ModelForm): 
    # regular fields, not based on bools 
    # ... 

    class Meta: 
     model = Order 
     exclude = ('work_type', 'vendor', 'note') 
     # or fields = (...other fields) 


    def __init__(
     self, 
     user, 
     can_edit_work_type=None, 
     can_edit_vendor=None, 
     can_edit_note=None, 
     *args, 
     **kwargs, 
    ): 
     super(OrderDetailForm, self).__init__(*args, **kwargs) 

     if can_edit_work_type: 
      self.fields['work_type'] = forms.ChoiceField(
             choices=Order.WORK_TYPE_CHOICES) 
     if can_edit_vendor: 
      self.fields['vendor'] = forms.ModelChoiceField(
       queryset=Vendor.objects.all(), 
       empty_label="Choose a vendor", 
      ) 
     if can_edit_note: 
      self.fields['note'] = forms.CharField(widget=forms.Textarea) 

    def clean(self): 
     # I never call super() in clean .. do I? .. hmmm 
     # maybe I should or is sth magic going on? 
     # alternately, 
     # data = self.cleaned_data 
     # let's call super though 
     data = super(OrderDetailForm, self).clean() 

     if 'note' in data: 
      if len(data['note']) < 50: 
       # I raise a validation error so .is_valid() comes back False 
       # form.errors happens magically ... 
       raise forms.ValidationError("Not long enough ...") 

     return data 

    def save(self, *args, **kwargs): 
     data = self.cleaned_data 
     # maybe do some stuff here 
     # ... 

     # commit=True or commit=False could be important 
     order = super(OrderDetailForm, self).save(*args, **kwargs) 

     if 'note' in data: 
      order.note = data['note'] 

     # ... do other stuff 

     # probably ... 
     order.save() 

     # respect how model forms work. 
     return order 
+0

那麼,我怎樣才能將模型字段添加到'__init__'下的'fields'?我可以做一些像'self.fields.append('note')'嗎?看起來'fields'仍然是一個元組,而不是字典。 – hobbes3

+0

你能在我的答案中給我看一個例子嗎?用代碼更新了 – hobbes3

+0

。你爲什麼認爲'fields'是一個'元組'? –

相關問題