python Django MultiWidget 电话号码字段
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1777435/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Django MultiWidget Phone Number Field
提问by Birdman
I want to create a field for phone number input that has 2 text fields (size 3, 3, and 4 respectively) with the common "(" ")" "-" delimiters. Below is my code for the field and the widget, I'm getting the following error when trying to iterate the fields in my form during initial rendering (it happens when the for loop gets to my phone number field):
我想为电话号码输入创建一个字段,该字段具有 2 个文本字段(大小分别为 3、3 和 4),并带有常见的“(”“)”“-”分隔符。下面是我的字段和小部件代码,在初始渲染期间尝试迭代表单中的字段时出现以下错误(当 for 循环到达我的电话号码字段时会发生这种情况):
Caught an exception while rendering: 'NoneType' object is unsubscriptable
渲染时捕获异常:“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)
采纳答案by Birdman
I took hughdbrown's advise and modified USPhoneNumberField to do what I need. The reason I didn't use it initially was that it stores phone numbers as XXX-XXX-XXXX in the DB, I store them as XXXXXXXXXX. So I over-rode the clean method:
我接受了 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'])
回答by derek73
This uses widget.value_from_datadict()
to format the data so no need to subclass a field, just use the existing USPhoneNumberField
. Data is stored in db like XXX-XXX-XXXX.
这用于widget.value_from_datadict()
格式化数据,因此无需对字段进行子类化,只需使用现有的USPhoneNumberField
. 数据存储在 db 中,如 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)
use in a form like so:
以这样的形式使用:
from django.contrib.localflavor.us.forms import USPhoneNumberField
class MyForm(forms.Form):
phone = USPhoneNumberField(label="Phone", widget=USPhoneNumberMultiWidget())
回答by rhu
I think the value_from_datadict() code can be simplified to:
我认为 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
The value_from_datadict() method for MultiValueWidget already does the following:
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)]
回答by Bratt
Sometimes it is useful to fix the original problem rather than redoing everything. The error you got, "Caught an exception while rendering: 'NoneType' object is unsubscriptable" has a clue. There is a value returned as None(unsubscriptable) when a subscriptable value is expected. The decompress function in PhoneNumberWidget class is a likely culprit. I would suggest returning [] instead of None.
有时,修复原始问题而不是重做所有事情是有用的。您收到的错误“渲染时捕获异常:'NoneType' 对象不可订阅”有一个线索。当需要可下标的值时,会返回一个作为 None(unsubscriptable) 的值。PhoneNumberWidget 类中的解压缩函数可能是罪魁祸首。我建议返回 [] 而不是 None。