【问题标题】:Django MultiWidget Phone Number FieldDjango MultiWidget 电话号码字段
【发布时间】:2010-12-19 03:28:46
【问题描述】:

我想为电话号码输入创建一个字段,该字段具有 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)

【问题讨论】:

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


【解决方案1】:

我认为 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)]

【讨论】:

  • 这很好用,但我在 Django 1.3 中的 value_from_datadict 函数中出现错误,因为 values 变量不是元组而是列表。我必须使用 tuple(values) 转换列表以使其正常工作。我相应地编辑了答案。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    这使用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())
    

    【讨论】:

      【解决方案4】:

      我接受了 Hughdbrown 的建议并修改了 USPhoneNumberField 来满足我的需要。我最初没有使用它的原因是它在数据库中将电话号码存储为 XXX-XXX-XXXX,我将它们存储为 XXXXXXXXXX。所以我放弃了 clean 方法:

      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'])
      

      【讨论】:

        猜你喜欢
        • 2017-02-08
        • 2021-12-02
        • 2020-02-02
        • 2020-12-10
        • 1970-01-01
        • 1970-01-01
        • 2021-08-15
        • 1970-01-01
        • 2020-04-05
        相关资源
        最近更新 更多