Поле MultiValueField
Класс MultiValueField применяется для создания полей формы, которые представляют собой комбинацию нескольких подполей. Это поле используется, когда необходимо собрать данные из нескольких полей ввода и обработать их как единое составное значение. Поле принимает список дочерних полей, каждый из которых отвечает за свою часть данных.
Синтаксис
from django import forms
class MultiValueField(fields, widget=None, **kwargs)
Основные параметры конструктора:
-
fields- список экземпляров полей, которые будут составлять составное поле -
widget- виджет для отображения составного поля (по умолчаниюMultiWidget) -
required- определяет, является ли поле обязательным для заполнения (по умолчаниюTrue)
Общее описание
MultiValueField является абстрактным базовым классом, который требует переопределения метода compress. Этот метод отвечает за преобразование списка валидированных значений из дочерних полей в единое составное значение. Поле автоматически обрабатывает валидацию каждого дочернего поля и собирает ошибки в общий список.
Пример
Рассмотрим создание составного поля для ввода телефонного номера, состоящего из кода страны и основного номера:
from django import forms
from django.core.exceptions import ValidationError
class PhoneNumberField(forms.MultiValueField):
def __init__(self, **kwargs):
fields = (
forms.CharField(max_length=5, label='Country code'),
forms.CharField(max_length=15, label='Phone number'),
)
widget = forms.MultiWidget(
widgets=(
forms.TextInput(attrs={'placeholder': '+7'}),
forms.TextInput(attrs={'placeholder': '123-45-67'}),
)
)
super().__init__(fields=fields, widget=widget, **kwargs)
def compress(self, data_list):
if data_list:
country_code = data_list[0]
phone_number = data_list[1]
if not phone_number:
raise ValidationError('Phone number is required')
return f'{country_code}{phone_number}'
return None
Теперь используем это поле в форме:
from django import forms
class ContactForm(forms.Form):
name = forms.CharField(max_length=100)
phone = PhoneNumberField()
form = ContactForm(data={
'name': 'John Doe',
'phone_0': '+7',
'phone_1': '123-45-67',
})
if form.is_valid():
res = form.cleaned_data
print(res)
Результат выполнения кода:
{'name': 'John Doe', 'phone': '+7123-45-67'}
Пример
Создадим составное поле для ввода диапазона цен с минимальным и максимальным значениями:
from django import forms
from django.core.exceptions import ValidationError
class PriceRangeField(forms.MultiValueField):
def __init__(self, **kwargs):
fields = (
forms.DecimalField(max_value=1000000, label='Min price'),
forms.DecimalField(max_value=1000000, label='Max price'),
)
widget = forms.MultiWidget(
widgets=(
forms.NumberInput(attrs={'placeholder': 'from'}),
forms.NumberInput(attrs={'placeholder': 'to'}),
)
)
super().__init__(fields=fields, widget=widget, **kwargs)
def compress(self, data_list):
if data_list:
min_price = data_list[0]
max_price = data_list[1]
if min_price is not None and max_price is not None:
if min_price > max_price:
raise ValidationError('Min price cannot be greater than max price')
return {'min': min_price, 'max': max_price}
return None
Использование поля в форме:
from django import forms
class ProductFilterForm(forms.Form):
category = forms.ChoiceField(choices=[('electronics', 'Electronics'), ('books', 'Books')])
price_range = PriceRangeField(required=False)
form = ProductFilterForm(data={
'category': 'electronics',
'price_range_0': '100',
'price_range_1': '500',
})
if form.is_valid():
res = form.cleaned_data
print(res)
Результат выполнения кода:
{'category': 'electronics', 'price_range': {'min': 100, 'max': 500}}
Пример
Рассмотрим обработку ошибок валидации в составном поле для ввода времени с часами и минутами:
from django import forms
from django.core.exceptions import ValidationError
class TimeField(forms.MultiValueField):
def __init__(self, **kwargs):
fields = (
forms.IntegerField(min_value=0, max_value=23, label='Hour'),
forms.IntegerField(min_value=0, max_value=59, label='Minute'),
)
widget = forms.MultiWidget(
widgets=(
forms.NumberInput(attrs={'placeholder': 'HH'}),
forms.NumberInput(attrs={'placeholder': 'MM'}),
)
)
super().__init__(fields=fields, widget=widget, **kwargs)
def compress(self, data_list):
if data_list:
hour = data_list[0]
minute = data_list[1]
if hour is None or minute is None:
raise ValidationError('Both hour and minute are required')
return f'{hour:02d}:{minute:02d}'
return None
Проверим форму с некорректными данными:
from django import forms
class ScheduleForm(forms.Form):
event_name = forms.CharField(max_length=200)
start_time = TimeField()
form = ScheduleForm(data={
'event_name': 'Meeting',
'start_time_0': '25',
'start_time_1': '70',
})
if form.is_valid():
res = form.cleaned_data
print(res)
else:
res = form.errors
print(res)
Результат выполнения кода:
{'start_time': ['Ensure this value is less than or equal to 23.', 'Ensure this value is less than or equal to 59.']}
Смотрите также
-
поле
SplitDateTimeField,
которое является встроенной реализацией составного поля для даты и времени -
класс
MultiWidget,
который используется для отображения нескольких виджетов в одном поле -
поле
ComboField,
которое объединяет несколько валидаторов для одного поля -
метод
clean,
который используется для валидации нескольких полей формы