2011-09-15 71 views
4

我想了解如何通過繼承MultiWidgets和MultiValueFields來創建表單。 我有一個簡單的地址模式和相關的形式:使用MultiWidget和MultiValueField保存表單模型

class Address(models.Model): 
    user = models.ForeignKey(User) 
    city = models.CharField(max_length=255) 
    state = models.CharField(choices = settings.STATES, max_length=50) 
    postal = models.CharField(max_length=10) 
    address = models.TextField() 

    class Meta: 
     verbose_name_plural = 'Addresses' 

class AddressFieldWidget(forms.MultiWidget): 
    def decompress(self,value): 
     if value: 
      return [value[0],value[1],value[2]] 
     return '' 

    def format_output(self, rendered_widgets): 
     str = '' 
     line_1 = '<td class="align_left"><label for="contact_phone">Address Line 1</label></td>' 

     for field in rendered_widgets: 
      str += '<tr>' + line_1 
      str += '<td class="align_right">%s</td></tr>' % field 
     return '<tr>' + str + '</tr>' 

    def value_from_datadict(self,data,files,name): 
     line_list = [widget.value_from_datadict(data,files,name+'_%s' %i) for i,widget in enumerate(self.widgets)] 
     try: 
      return line_list[0] + ' ' + line_list[1] + ' ' + line_list[2]  
     except: 
      return '' 

class AddressField(forms.MultiValueField): 
    def __init__(self,*args,**kwargs): 
     fields = (
      forms.CharField(widget=forms.TextInput(attrs={'class':'big'})), 
      forms.CharField(widget=forms.TextInput(attrs={'class':'big'})), 
      forms.CharField(widget=forms.TextInput(attrs={'class':'big'})), 
     ) 
     super(AddressField,self).__init__(*args,**kwargs) 
     self.widget = AddressFieldWidget(widgets=[fields[0].widget, fields[1].widget, fields[2].widget]) 

    def compress(self, data_list): 
     return data_list[0] + ' ' + data_list[1] + ' ' + data_list[2] 


class AddressFormNew(forms.ModelForm): 
    postal = forms.CharField(widget=forms.TextInput(attrs={'class':'small'})) 
    address = AddressField() 
    city = forms.CharField(widget=forms.TextInput(attrs={'class':'big'})) 

    class Meta: 
     model = Address 

嗯,我無法弄清楚如何在我看來,使用這種形式。我試圖做的事:

@login_required 
def render_addresses(request): 
    address_form = AddressFormNew() 
    if request.method == 'POST': 
     address_form = AddressFormNew(request.POST) 
     if address_form.is_valid(): 
      address_form.save() 
      return HttpResponse('ok') 
     else: 
      return HttpResponse(address_form.errors['address']) 

    return render_to_response('profile/addresses.html',context_instance=RequestContext(request,{'address_form':address_form})) 

其結果是,Django的給了我這個錯誤:

Enter a list of values.

此外,當我嘗試打印request.POST.items(),它給了地址響應作爲3個獨立的數據。

我很迷茫,我必須把我的地址數據放在一行。我應該如何通過只保存表格來實現這一目標?

我真的很感謝,如果有人給我一個清晰的解釋。

回答

0

我想你也需要一個解壓縮方法和字段=(...)應該是外面的__init__

+0

我有一個解壓縮方法,我認爲value_from_datadict導致的問題,但我不知道如何使用它? – iva123

-1

Django的源文件往往是靈感的好來源。 (沒有打算的雙關語)你可以例如檢查出django.forms.fields.SplitDateTimeField,這給出了一個如何做類似的例子。

一些可能的錯誤可能是您在初始化後設置self.widget(super(AddressField,self).__init__()),所以該字段只使用標準小部件。而且您也沒有將fields__init__一起發送。下面是如何,我認爲你可以做AddressField快速草案:

class AddressField(forms.MultiValueField): 
    widget = MultiValueWidget(widgets=(forms.TextInput, forms.TextInput, forms.TextInput) 
    def __init__(self, *args, **kwargs): 
     fields = (
      forms.CharField(widget=forms.TextInput(attrs={'class':'big'})), 
      forms.CharField(widget=forms.TextInput(attrs={'class':'big'})), 
      forms.CharField(widget=forms.TextInput(attrs={'class':'big'})), 
     ) 
     super(AddressField,self).__init__(fields, *args, **kwargs) 

    def compress(self, data_list): 
     return "%s %s %s" % data_list[0:2] 

這排除所有天書你AddressFieldWidget了,因爲坦白說,我不太明白你試圖做:)

4

以下是我在代碼中看到的應解決的問題:

(1)。在您的AddressField init方法當您調用init超級類別時,您應該通過字段作爲參數。

class AddressField(forms.MultiValueField): 
    def __init__(self,*args,**kwargs): 
     fields = (
      forms.CharField(widget=forms.TextInput(attrs={'class':'big'})), 
      forms.CharField(widget=forms.TextInput(attrs={'class':'big'})), 
      forms.CharField(widget=forms.TextInput(attrs={'class':'big'})), 
     ) 
     self.widget = AddressFieldWidget(widgets=[fields[0].widget, fields[1].widget, fields[2].widget]) 
     super(AddressField,self).__init__(fields=fields,*args,**kwargs) 

(2)。你是對的,你的value_from_datadict不正確。重點在於,你已經使用了一個MultiValueField來獲得這個小部件的填充。所以,小部件必須AddressField

返回值的列表對應的子場

您只需撥打value_from_datadict的超類的,並且將做的工作,或使用(我認爲是相同):

def value_from_datadict(self,data,files,name): 
    res = [] 
    for i, widget in enumerate(self.widgets): 
     res.append(widget.value_from_datadict(data, files, name + '_%s' % i)) 
    return res 

它對了解底層概念很重要。你也可以在CharField中使用這個小部件。在這種情況下,value_from_datadict應該返回一個字符串。但是,由於您使用的是MultiValueField,所以返回類型應該是一個列表。這就是獲取「輸入值列表」的錯誤原因

只是另外一個想法,如果您打算根據存儲的值重新創建地址行1,2和3,則不應將空格用作分隔符在數據庫中形成一個表單。如果不是那麼一切都很好:)

我沒有在文檔或網絡上找到MultiValueField和MultiWidget的好例子,但是因爲我必須在我的一個項目中使用它們,所以我不得不自己挖掘它。希望這有助於:)

相關問題