Поле за телефонен номер на Django MultiWidget

Искам да създам поле за въвеждане на телефонен номер, което има 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)

person Birdman    schedule 22.11.2009    source източник
comment
Има ли причина да не използвате само us.models.PhoneNumberField и us.forms.USPhoneNumberField? Много удобно, ако имате телефонни номера в САЩ. docs.djangoproject.com/en /dev/ref/contrib/localflavor/   -  person hughdbrown    schedule 22.11.2009
comment
Би било полезно да знаете къде се е случвало проследяването - т.е. дайте повече подробности от този един ред.   -  person Daniel Roseman    schedule 22.11.2009
comment
Във връзка с предложението, направено от @hughdbrown Django-Localflavor беше преместен в Django 1.5, сега се намира в github. com/django/django-localflavor   -  person davelupt    schedule 31.08.2015


Отговори (4)


Това използва 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)

използвайте във форма като тази:

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

Мисля, че кодът 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

Методът value_from_datadict() за MultiValueWidget вече прави следното:


    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)]
person rhu    schedule 26.10.2010
comment
това работи добре, но получавам грешка в Django 1.3 във функцията value_from_datadict, тъй като променливата стойности не е кортеж, а списък. Трябваше да преобразувам списъка с помощта на tuple(values), за да работи правилно. Редактирах отговора съответно. - person bchhun; 15.06.2011

Приех съвета на hughdbrown и модифицирах USPhoneNumberField, за да направи това, от което се нуждая. Причината, поради която не го използвах първоначално, беше, че съхранява телефонни номера като XXX-XXX-XXXX в DB, ​​аз ги съхранявам като 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'])
person Birdman    schedule 22.11.2009

Понякога е полезно да коригирате първоначалния проблем, вместо да правите всичко отново. Грешката, която сте получили, „Уловено изключение при рендиране: обектът „NoneType“ не може да се абонира“ има представа. Има стойност, върната като None(unsubscriptable), когато се очаква стойност за subscriptable. Функцията за декомпресиране в класа PhoneNumberWidget е вероятен виновник. Бих предложил да върнете [] вместо None.

person Bratt    schedule 25.10.2010