Skip to content Skip to sidebar Skip to footer

Django Multiwidget Phone Number Field

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 t

Solution 1:

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.

from django import forms

classUSPhoneNumberMultiWidget(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)

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

    defvalue_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 valuesfor d infilter(lambda x: x.startswith(name), data):
            index = int(d[len(name)+1:]) 
            value[index] = data[d]
        if value[0] == value[1] == value[2] == u'':
            returnNonereturnu'%s-%s-%s' % tuple(value)

use in a form like so:

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

Solution 2:

I think the value_from_datadict() code can be simplified to:

classUSPhoneNumberMultiWidget(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)

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

    defvalue_from_datadict(self, data, files, name):
        values = super(USPhoneNumberMultiWidget, self).value_from_datadict(data, files, name)
        returnu'%s-%s-%s' % values

The value_from_datadict() method for MultiValueWidget already does the following:

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

Solution 3:

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:

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

Solution 4:

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.

Post a Comment for "Django Multiwidget Phone Number Field"