4

我试图了解如何通过子类化 MultiWidgets 和 MultiValueFields 来创建表单。我有一个简单的地址模型和相关表格:

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给了我这个错误:

输入值列表。

此外,当我尝试打印 request.POST.items() 时,它会将地址响应作为 3 个单独的数据提供。

我在这里很迷茫,我必须在一行中获取我的地址数据。我应该如何通过只保存我的表单来实现这一点?

如果有人给我一个明确的解释,我真的很感激。

4

3 回答 3

5

以下是我在您的代码中看到的应该解决的问题:

(1)。在调用超类的init时的 AddressField 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,因此返回类型应该是一个列表。这就是将“输入值列表”作为错误的原因

只是一个额外的想法,如果您计划从存储在数据库中的值重新创建地址 line1、2 和 3 到表单,则不应使用空格作为分隔符。如果不是那么一切都很好:)

我没有在文档或网络上找到 MultiValueField 和 MultiWidget 的好例子,但由于我必须在我的一个项目中使用它们,我不得不自己深入研究它。希望这可以帮助 :)

于 2011-09-21T01:23:18.483 回答
0

i think you also need a decompress method, and fields = (...) should be outside of __init__

于 2011-09-16T03:07:19.610 回答
-1

Django 的源文件通常可以成为很好的灵感来源。(双关语不是有意的)您可以例如 check out django.forms.fields.SplitDateTimeField,它给出了一个如何做类似事情的例子。

一些可能的错误可能是您在初始化 ( super(AddressField,self).__init__()) 之后设置了 self.widget,因此该字段仅使用标准小部件。而且您也没有发送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]

这不包括你所拥有的所有 mumbo-jumbo AddressFieldWidget,因为坦率地说,我不太明白你想要做什么:)

于 2011-09-19T18:10:18.810 回答