Lidando com vários valores de entrada para um único campo de formulário Django

Imagine este cenário: você deve escrever um formulário que trata do endereço do cliente, com um campo para número de telefone, incluindo código de área.

from django import forms

class PhoneForm(forms.Form):
# very basic
number
= forms.CharField()
area_code
= forms.CharField()

Parece fácil? E se você tiver que escrever HTML com duas entradas, uma para número de telefone e outra para código de área, mas no banco de dados houver apenas um campo, para um número de telefone com código de área?

A primeira resposta seria lidar com isso no método limpo, assim:

class PhoneForm(forms.ModelForm):
number
= forms.CharField()
area_code
= forms.CharField()

def clean(self):
number
= self.cleaned_data['number']
code
= self.cleaned_data['area_code']
self.cleaned_data['full_number'] = code + number

Mas isso é: a) desagradável b) inextensível. Você não pode simplesmente reutilizar qualquer um desses códigos e manter a ação sem limpar nada diretamente no método de limpeza.

Mas, há uma maneira melhor – entre no Django MultiValueField

Para usá-lo efetivamente, precisamos criar uma subclasse de MultiValueField e MultiWidget . Em seguida, você define a subclasse do widget como o widget da subclasse de campo, para que possa manipular e exibir os dados.

A última coisa a lembrar é que você precisa implementar métodos de compactação e descompactação para campo e widget, respectivamente. Isso ocorre porque o campo deve retornar um único valor e recebe um único , não importa quantas entradas você realmente exibe ou quantos dados são enviados.

Por exemplo

class PhoneNumberWidget(forms.MultiWidget):
def __init__(self, *args, **kwargs):
super(PhoneNumberWidget, self).__init__(*args, **kwargs)
self.widgets = [
forms
.TextInput(),
forms
.TextInput()
]
def decompress(self, value):
if value:
return value.split(' ')
return [None, None]

class PhoneNumberField(forms.MultiValueField):
widget
= PhoneNumberWidget

def __init__(self, *args, **kwargs):
super(PhoneNumberField, self).__init__(*args, **kwargs)
fields
= (
forms
.CharField(),
forms
.CharField()
)

def compress(self, data_list):
return ' '.join(data_list)


class PhoneForm(forms.ModelForm):
phone_number
= PhoneNumberField()

def __init__(self, *args, **kwargs):
super(PhoneForm, self).__init__(self, *args, **kwargs)
self.initial['phone_number'] = ['+1','11111111']

Isso é um pouco mais de código, mas permite que você envolva bem a lógica de negócios com o código. Se você precisar de um campo para lidar com um número de telefone com código de área dessa forma em qualquer outro lugar, pode apenas importar o campo em vez de usar o método copiar e colar .