Quiero crear un campo para la entrada del número de teléfono que tenga 2 campos de texto (tamaño 3, 3 y 4 respectivamente) con los delimitadores comunes "(" ")" "-". A continuación se muestra mi código para el campo y el widget, obtengo el siguiente error al intentar iterar los campos en mi formulario durante la representación inicial (ocurre cuando el bucle for llega al campo de mi número de teléfono):

Se detectó una excepción mientras se procesaba: el objeto 'NoneType' no se puede suscribir

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)
7
Birdman 22 nov. 2009 a las 04:17

4 respuestas

La mejor respuesta

Tomé el consejo de hughdbrown y modifiqué USPhoneNumberField para hacer lo que necesito. La razón por la que no lo usé inicialmente fue porque almacena números de teléfono como XXX-XXX-XXXX en la base de datos, los guardo como XXXXXXXXXX. Así que superé el método limpio:

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'])
1
Birdman 22 nov. 2009 a las 18:26

A veces es útil solucionar el problema original en lugar de rehacer todo. El error que obtuvo, "Capturó una excepción al procesar: el objeto 'NoneType' no tiene suscriptos" tiene una pista. Hay un valor devuelto como None (unsubscripttable) cuando se espera un valor subscripttable. La función de descompresión en la clase PhoneNumberWidget es probablemente el culpable. Sugeriría devolver [] en lugar de None.

0
Bratt 25 oct. 2010 a las 12:14

Creo que el código value_from_datadict () se puede simplificar a:


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

El método value_from_datadict () para MultiValueWidget ya hace lo siguiente:


    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)]
4
rhu 26 oct. 2010 a las 15:55

Esto usa widget.value_from_datadict() para formatear los datos para que no sea necesario subclasificar un campo, solo use el USPhoneNumberField existente. Los datos se almacenan en db como 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)

Utilizar en una forma como esta:

from django.contrib.localflavor.us.forms import USPhoneNumberField
class MyForm(forms.Form):
    phone = USPhoneNumberField(label="Phone", widget=USPhoneNumberMultiWidget())
6
derek73 16 dic. 2009 a las 06:21