Django MultiWidget电话号码字段

时间:2009-11-22 01:17:30

标签: python django-templates django-forms django-widget django-models

我想为电话号码输入创建一个字段,该字段包含2个文本字段(分别为3,3和4),并带有公共“(”“)” - “ - 分隔符。下面是我的字段和小部件的代码,我在初始渲染期间尝试迭代表单中的字段时遇到以下错误(当for循环到达我的电话号码字段时发生):

渲染时捕获异常:'NoneType'对象是不可取消的

class PhoneNumberWidget(forms.MultiWidget):
    def __init__(self,attrs=None):
        wigs = (forms.TextInput(attrs={'size':'3','maxlength':'3'}),\
                forms.TextInput(attrs={'size':'3','maxlength':'3'}),\
                forms.TextInput(attrs={'size':'4','maxlength':'4'}))
        super(PhoneNumberWidget, self).__init__(wigs, attrs)

    def decompress(self, value):
        return value or None

    def format_output(self, rendered_widgets):
        return '('+rendered_widgets[0]+')'+rendered_widgets[1]+'-'+rendered_widgets[2]

class PhoneNumberField(forms.MultiValueField):
    widget = PhoneNumberWidget
    def __init__(self, *args, **kwargs):
        fields=(forms.CharField(max_length=3), forms.CharField(max_length=3), forms.CharField(max_length=4))
        super(PhoneNumberField, self).__init__(fields, *args, **kwargs)
    def compress(self, data_list):
        if data_list[0] in fields.EMPTY_VALUES or data_list[1] in fields.EMPTY_VALUES or data_list[2] in fields.EMPTY_VALUES:
            raise fields.ValidateError(u'Enter valid phone number')
        return data_list[0]+data_list[1]+data_list[2]

class AdvertiserSumbissionForm(ModelForm):
    business_phone_number = PhoneNumberField(required=True)

4 个答案:

答案 0 :(得分:6)

这使用widget.value_from_datadict()格式化数据,因此不需要对字段进行子类化,只需使用现有的USPhoneNumberField。数据存储在数据库中,如XXX-XXX-XXXX。

from django import forms

class USPhoneNumberMultiWidget(forms.MultiWidget):
    """
    A Widget that splits US Phone number input into three <input type='text'> boxes.
    """
    def __init__(self,attrs=None):
        widgets = (
            forms.TextInput(attrs={'size':'3','maxlength':'3', 'class':'phone'}),
            forms.TextInput(attrs={'size':'3','maxlength':'3', 'class':'phone'}),
            forms.TextInput(attrs={'size':'4','maxlength':'4', 'class':'phone'}),
        )
        super(USPhoneNumberMultiWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            return value.split('-')
        return (None,None,None)

    def value_from_datadict(self, data, files, name):
        value = [u'',u'',u'']
        # look for keys like name_1, get the index from the end
        # and make a new list for the string replacement values
        for d in filter(lambda x: x.startswith(name), data):
            index = int(d[len(name)+1:]) 
            value[index] = data[d]
        if value[0] == value[1] == value[2] == u'':
            return None
        return u'%s-%s-%s' % tuple(value)

以这样的形式使用:

from django.contrib.localflavor.us.forms import USPhoneNumberField
class MyForm(forms.Form):
    phone = USPhoneNumberField(label="Phone", widget=USPhoneNumberMultiWidget())

答案 1 :(得分:4)

我认为value_from_datadict()代码可以简化为:


class USPhoneNumberMultiWidget(forms.MultiWidget):
    """
    A Widget that splits US Phone number input into three  boxes.
    """
    def __init__(self,attrs=None):
        widgets = (
            forms.TextInput(attrs={'size':'3','maxlength':'3', 'class':'phone'}),
            forms.TextInput(attrs={'size':'3','maxlength':'3', 'class':'phone'}),
            forms.TextInput(attrs={'size':'4','maxlength':'4', 'class':'phone'}),
        )
        super(USPhoneNumberMultiWidget, self).__init__(widgets, attrs)

    def decompress(self, value):
        if value:
            return value.split('-')
        return [None,None,None]

    def value_from_datadict(self, data, files, name):
        values = super(USPhoneNumberMultiWidget, self).value_from_datadict(data, files, name)
        return u'%s-%s-%s' % values

MultiValueWidget的value_from_datadict()方法已执行以下操作:


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

答案 2 :(得分:1)

我接受了hughdbrown的建议并修改了USPhoneNumberField来做我需要的事情。我最初没有使用它的原因是它将电话号码存储为数据库中的XXX-XXX-XXXX,我将它们存储为XXXXXXXXXX。所以我过度使用了干净的方法:

class PhoneNumberField(USPhoneNumberField):
    def clean(self, value):
        super(USPhoneNumberField, self).clean(value)
        if value in EMPTY_VALUES:
            return u''
        value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
        m = phone_digits_re.search(value)
        if m:
            return u'%s%s%s' % (m.group(1), m.group(2), m.group(3))
        raise ValidationError(self.error_messages['invalid'])

答案 3 :(得分:0)

有时修复原始问题而不是重做所有内容是有用的。你得到的错误,“渲染时捕获异常:'NoneType'对象是unsubscriptable”有一个线索。当预期可订阅的值时,返回值为None(unsubscriptable)。 PhoneNumberWidget类中的解压缩功能可能是罪魁祸首。我建议返回[]而不是无。