Виджеты
Виджет — это представление Django для элемента HTML-ввода. Виджет обрабатывает рендеринг HTML и извлечение данных из словаря GET/POST, соответствующего виджету.
Подсказка
Не следует путать виджеты с полями формы. Поля формы обрабатывают логику проверки ввода и используются непосредственно в шаблонах. Виджеты занимаются рендерингом элементов ввода HTML-формы на веб-странице и извлечением необработанных данных, отправленных пользователем. Однако виджеты необходимо присваивать полям формы.
Указание виджетов
Когда вы указываете поле в форме, Django использует по умолчанию виджет, подходящий для типа отображаемых данных. Чтобы узнать, какой виджет используется для какого поля, ознакомьтесь с документацией по встроенным классам полей.
Однако, если вы хотите использовать другой виджет для поля, вы можете просто использовать аргумент widget в определении поля. Например:
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField(widget=forms.Textarea)
Это определит форму с полем комментария, использующим виджет Textarea большего размера вместо виджета TextInput по умолчанию.
Установка аргументов для виджетов
У многих виджетов есть дополнительные необязательные аргументы; они могут быть установлены при определении виджета в поле. В следующем примере устанавливается атрибут years для виджета SelectDateWidget:
from django import forms
BIRTH_YEAR_CHOICES = ('1980', '1981', '1982')
FAVORITE_COLORS_CHOICES = (
('blue', 'Blue'),
('green', 'Green'),
('black', 'Black'),
)
class SimpleForm(forms.Form):
birth_year = forms.DateField(widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICES))
favorite_colors = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=FAVORITE_COLORS_CHOICES,
)
Дополнительную информацию о доступных виджетах и принимаемых ими аргументах см. в разделе Встроенные виджеты.
Виджеты, наследующие от виджета Select
Виджеты, наследующие от виджета Select, работают с выбором. Они предоставляют пользователю список вариантов для выбора. Разные виджеты представляют этот выбор по-разному; виджет Select использует представление списка HTML <select>, а виджет RadioSelect — радиокнопки.
Виджеты Select по умолчанию используются для полей ChoiceField. Выбор, отображаемый в виджете, наследуется от поля ChoiceField, и изменение ChoiceField.choices обновит Select.choices. Например:
>>> from django import forms
>>> CHOICES = (('1', 'First',), ('2', 'Second',))
>>> choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES)
>>> choice_field.choices
[('1', 'First'), ('2', 'Second')]
>>> choice_field.widget.choices
[('1', 'First'), ('2', 'Second')]
>>> choice_field.widget.choices = ()
>>> choice_field.choices = (('1', 'First and only',),)
>>> choice_field.widget.choices
[('1', 'First and only')]
Однако виджеты, которые предлагают атрибут choices, могут использоваться с полями, не основанными на выборе — такими как CharField — но рекомендуется использовать поле, основанное на ChoiceField, когда варианты выбора связаны с моделью, а не только с виджетом.
Настройка экземпляров виджетов
Когда Django рендерит виджет как HTML, он рендерит только минимальный разметку — Django не добавляет имена классов или другие специфичные для виджета атрибуты. Это означает, например, что все виджеты TextInput будут выглядеть одинаково на ваших веб-страницах.
Существует два способа настройки виджетов: на уровне экземпляра виджета и на уровне класса виджета.
Стилизация экземпляров виджетов
Если вы хотите, чтобы один экземпляр виджета выглядел иначе, чем другой, вам необходимо указать дополнительные атрибуты при создании объекта виджета и присвоении его полю формы (и, возможно, добавить некоторые правила в файлы CSS).
Например, рассмотрим следующую простую форму:
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField()
Эта форма будет содержать три виджета TextInput по умолчанию с рендерингом по умолчанию — без класса CSS и без дополнительных атрибутов. Это означает, что поля ввода для каждого виджета будут отображаться точно так же:
>>> f = CommentForm(auto_id=False) >>> f.as_table() <tr><th>Name:</th><td><input type="text" name="name" /></td></tr> <tr><th>Url:</th><td><input type="url" name="url"/></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
На реальной веб-странице вы, вероятно, не захотите, чтобы каждый виджет выглядел одинаково. Возможно, вам потребуется больший элемент ввода для комментария, и, возможно, виджет «имя» должен иметь специальный класс CSS. Также можно указать атрибут «type», чтобы воспользоваться новыми типами ввода HTML5. Для этого используйте аргумент Widget.attrs при создании виджета:
class CommentForm(forms.Form):
name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
url = forms.URLField()
comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))
Тогда Django включит дополнительные атрибуты в выводимый результат:
>>> f = CommentForm(auto_id=False) >>> f.as_table() <tr><th>Name:</th><td><input type="text" name="name" class="special"/></td></tr> <tr><th>Url:</th><td><input type="url" name="url"/></td></tr> <tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
Вы также можете задать HTML id с помощью attrs. См. BoundField.id_for_label для примера.
Стилизация классов виджетов
С виджетами можно добавлять ресурсы (css и javascript) и более глубоко настраивать их внешний вид и поведение.
Вкратце, вам нужно будет унаследовать от виджета и либо определить внутренний класс «Media», либо создать свойство «media».
Эти методы подразумевают несколько продвинутое программирование на Python и подробно описаны в руководстве по теме Ресурсы форм.
Базовые классы виджетов
Базовые классы виджетов Widget и MultiWidget наследуются всеми встроенными виджетами и могут служить основой для пользовательских виджетов.
Widget
-
class Widget(attrs=None)[source] -
Этот абстрактный класс не может быть отображён, но предоставляет базовый атрибут
attrs. Вы также можете реализовать или переопределить методrender()в пользовательских виджетах.-
attrs -
Словарь, содержащий HTML-атрибуты, которые нужно установить для отображаемого виджета.
>>> from django import forms >>> name = forms.TextInput(attrs={'size': 10, 'title': 'Your name',}) >>> name.render('name', 'A name') '<input title="Your name" type="text" name="name" value="A name" size="10" />'Если вы присвоите значение
TrueилиFalseатрибуту, оно будет отображено как булев HTML5-атрибут:>>> name = forms.TextInput(attrs={'required': True}) >>> name.render('name', 'A name') '<input name="name" type="text" value="A name" required />' >>> >>> name = forms.TextInput(attrs={'required': False}) >>> name.render('name', 'A name') '<input name="name" type="text" value="A name" />'
-
supports_microseconds -
Атрибут, который по умолчанию равен
True. Если он установлен вFalse, часть микросекунд значенийdatetimeиtimeбудут установлены в0.В более старых версиях этот атрибут был определён только для виджетов даты и времени (как
False).
-
id_for_label(self, id_)[source] -
Возвращает атрибут HTML ID этого виджета для использования меткой
<label>, учитывая ID поля. ВозвращаетNoneесли ID недоступен.Этот метод необходим, потому что некоторые виджеты имеют несколько HTML-элементов и, следовательно, несколько ID. В этом случае этот метод должен вернуть значение ID, которое соответствует первому ID в тегах виджета.
-
render(name, value, attrs=None)[source] -
Возвращает HTML для виджета в виде строки Unicode. Этот метод должен быть реализован подклассом, в противном случае будет поднято исключение
NotImplementedError.Передаваемое значение «value» не гарантируется, что будет допустимым вводом, поэтому реализации подклассов должны быть защищены от ошибок.
-
value_from_datadict(data, files, name)[source] -
Учитывая словарь данных и имя этого виджета, возвращает значение этого виджета.
filesможет содержать данные, полученные изrequest.FILES. ВозвращаетNoneесли значение не было предоставлено. Также обратите внимание, чтоvalue_from_datadictможет вызываться более одного раза во время обработки данных формы, поэтому, если вы его настраиваете и добавляете дорогостоящую обработку, вам нужно реализовать механизм кэширования самостоятельно.
-
MultiWidget
-
class MultiWidget(widgets, attrs=None)[source] -
Виджет, состоящий из нескольких виджетов.
MultiWidgetработает в связке сMultiValueField.MultiWidgetтребует один обязательный аргумент:-
widgets -
Итерируемый объект, содержащий необходимые виджеты.
И один обязательный метод:
-
decompress(value)[source] -
Этот метод принимает одно «сжатое» значение из поля и возвращает список «разжатых» значений. Входное значение можно считать допустимым, но не обязательно непустым.
Этот метод должен быть реализован подклассом, и поскольку значение может быть пустым, реализация должна быть защищена от этого.
Принцип «разжатия» заключается в необходимости «разделения» комбинированного значения поля формы на значения для каждого виджета.
Пример этого – как
SplitDateTimeWidgetпреобразует значениеdatetimeв список, разбивая дату и время на два отдельных значения:from django.forms import MultiWidget class SplitDateTimeWidget(MultiWidget): # ... def decompress(self, value): if value: return [value.date(), value.time().replace(microsecond=0)] return [None, None]Подсказка
Обратите внимание, что у
MultiValueFieldесть дополнительный методcompress()с обратной ответственностью – объединить очищенные значения всех полей-членов в одно.
Другие методы, которые могут быть полезно переопределены:
-
render(name, value, attrs=None)[source] -
Аргумент
valueобрабатывается в этом методе иначе, чем в подклассахWidget, потому что он должен понять, как разбить одно значение для отображения в нескольких виджетах.Аргумент
valueиспользуемый при рендеринге, может быть одним из двух:- Список
list. - Одно значение (например, строка), которое является «сжатым» представлением
listзначений.
Если
valueявляется списком, то выходrender()будет конкатенацией рендеренных дочерних виджетов. Еслиvalueне является списком, он сначала обрабатывается методомdecompress()для создания списка, а затем рендерится.Когда
render()выполняет рендеринг HTML, каждое значение в списке рендерится с соответствующим виджетом – первое значение рендерится в первом виджете, второе значение во втором и т.д.В отличие от виджетов с одиночными значениями, метод
render()не обязательно реализовывать в подклассах. - Список
-
format_output(rendered_widgets)[source] -
Принимая на вход список рендеренных виджетов (как строки), возвращает строку Unicode, представляющую HTML для всего набора.
Этот крючок позволяет вам форматировать HTML-дизайн виджетов как вам нужно.
Вот пример виджета, который наследуется от
MultiWidgetдля отображения даты с днём, месяцем и годом в разных select-полях. Этот виджет предназначен для использования сDateField, а не сMultiValueField, поэтому мы реализовалиvalue_from_datadict():from datetime import date from django.forms import widgets class DateSelectorWidget(widgets.MultiWidget): def __init__(self, attrs=None): # create choices for days, months, years # example below, the rest snipped for brevity. years = [(year, year) for year in (2011, 2012, 2013)] _widgets = ( widgets.Select(attrs=attrs, choices=days), widgets.Select(attrs=attrs, choices=months), widgets.Select(attrs=attrs, choices=years), ) super(DateSelectorWidget, self).__init__(_widgets, attrs) def decompress(self, value): if value: return [value.day, value.month, value.year] return [None, None, None] def format_output(self, rendered_widgets): return ''.join(rendered_widgets) def value_from_datadict(self, data, files, name): datelist = [ widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)] try: D = date( day=int(datelist[0]), month=int(datelist[1]), year=int(datelist[2]), ) except ValueError: return '' else: return str(D)Конструктор создаёт несколько виджетов
Selectв кортеже. Классsuperиспользует этот кортеж для настройки виджета.Метод
format_output()здесь довольно простой (фактически, он такой же, как реализован по умолчанию дляMultiWidget), но идея в том, что вы можете добавить пользовательский HTML между виджетами, если захотите.Необходимый метод
decompress()разбивает значениеdatetime.dateна значения дня, месяца и года, соответствующие каждому виджету. Обратите внимание, как метод обрабатывает случай, когдаvalueравноNone.По умолчанию реализация
value_from_datadict()возвращает список значений, соответствующих каждомуWidget. Это уместно при использованииMultiWidgetсMultiValueField, но поскольку мы хотим использовать этот виджет сDateField, который принимает одно значение, мы переопределили этот метод, чтобы объединить данные всех дочерних виджетов в одноdatetime.date. Метод извлекает данные из словаряPOSTи создаёт, и валидирует дату. Если дата валидна, мы возвращаем строку, в противном случае возвращаем пустую строку, что заставитform.is_validвернутьFalse. -
Встроенные виджеты
Django предоставляет представление всех основных HTML-виджетов, а также некоторых часто используемых групп виджетов в модуле django.forms.widgets , включая виджеты для ввода текста, различные флажки и селекторы, загрузку файлов и обработку многозначных данных.
Виджеты для ввода текста
Эти виджеты используют HTML-элементы input и textarea.
TextInput
-
class TextInput[source] -
Ввод текста:
<input type="text" ...>
NumberInput
-
class NumberInput[source] -
Ввод текста:
<input type="number" ...>Обратите внимание, что не все браузеры поддерживают ввод локальных чисел в типах ввода
number. Сам Django избегает их использования для полей, у которых свойствоlocalizeустановлено вTrue.
EmailInput
-
class EmailInput[source] -
Ввод текста:
<input type="email" ...>
URLInput
-
class URLInput[source] -
Ввод текста:
<input type="url" ...>
PasswordInput
-
class PasswordInput[source] -
Ввод пароля:
<input type='password' ...>Принимает один необязательный аргумент:
-
render_value -
Определяет, будет ли значение виджета заполнено при повторном отображении формы после ошибки валидации (по умолчанию
False).
-
HiddenInput
-
class HiddenInput[source] -
Скрытый ввод:
<input type='hidden' ...>Обратите внимание, что также есть виджет
MultipleHiddenInput, который обобщает набор скрытых элементов ввода.
DateInput
-
class DateInput[source] -
Ввод даты в виде простого текстового поля:
<input type='text' ...>Принимает те же аргументы, что и
TextInput, плюс один дополнительный необязательный аргумент:-
format -
Формат, в котором будет отображаться начальное значение этого поля.
Если аргумент
formatне указан, используется первый формат, найденный вDATE_INPUT_FORMATS, и учитывается Локализация форматов. -
DateTimeInput
-
class DateTimeInput[source] -
Ввод даты и времени в виде простого текстового поля:
<input type='text' ...>Принимает те же аргументы, что и
TextInput, плюс один дополнительный необязательный аргумент:-
format -
Формат, в котором будет отображаться начальное значение этого поля.
Если аргумент
formatне указан, используется первый формат, найденный вDATETIME_INPUT_FORMATS, и учитывается Локализация форматов.По умолчанию, микросекундная часть значения времени всегда устанавливается в
0. Если требуются микросекунды, используйте подкласс с атрибутомsupports_microseconds, установленным вTrue. -
TimeInput
-
class TimeInput[source] -
Ввод времени в виде простого текстового поля:
<input type='text' ...>Принимает те же аргументы, что и
TextInput, плюс один дополнительный необязательный аргумент:-
format -
Формат, в котором будет отображаться начальное значение этого поля.
Если аргумент
formatне указан, используется первый формат, найденный вTIME_INPUT_FORMATS, и учитывается Локализация форматов.Обработка микросекунд описана в разделе
DateTimeInput. -
Textarea
-
class Textarea[source] -
Текстовая область:
<textarea>...</textarea>
Selector and checkbox widgets
CheckboxInput
-
class CheckboxInput[source] -
Флажок:
<input type='checkbox' ...>Принимает один необязательный аргумент:
-
check_test -
Функция, которая принимает значение
CheckboxInputи возвращаетTrue, если флажок должен быть отмечен для этого значения.
-
Select
-
class Select[source] -
Виджет выбора:
<select><option ...>...</select>-
choices -
Этот атрибут необязателен, если поле формы не имеет атрибута
choices. Если он есть, он переопределит всё, что вы установили здесь, когда атрибут обновляется вField.
-
NullBooleanSelect
-
class NullBooleanSelect[source] -
Виджет выбора с вариантами «Неизвестно», «Да» и «Нет»
SelectMultiple
-
class SelectMultiple[source] -
Аналогично
Select, но позволяет множественный выбор:<select multiple='multiple'>...</select>
RadioSelect
-
class RadioSelect[source] -
Аналогично
Select, но отображается как список радиокнопок внутри<li>тегов:<ul> <li><input type='radio' name='...'></li> ... </ul>
Для более точного управления генерируемой разметкой, вы можете перебирать радиокнопки в шаблоне. Предположим, у нас есть форма
myformс полемbeatles, которое используетRadioSelectв качестве виджета:{% for radio in myform.beatles %} <div class="myradio"> {{ radio }} </div> {% endfor %}Это сгенерирует следующий HTML:
<div class="myradio"> <label for="id_beatles_0"><input id="id_beatles_0" name="beatles" type="radio" value="john" /> John</label> </div> <div class="myradio"> <label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" /> Paul</label> </div> <div class="myradio"> <label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" /> George</label> </div> <div class="myradio"> <label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" /> Ringo</label> </div>Включая теги
<label>.Для более точного управления вы можете использовать атрибуты каждой радиокнопки
tag,choice_labelиid_for_label. Например, этот шаблон...{% for radio in myform.beatles %} <label for="{{ radio.id_for_label }}"> {{ radio.choice_label }} <span class="radio">{{ radio.tag }}</span> </label> {% endfor %}...приведёт к следующему HTML:
<label for="id_beatles_0"> John <span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" /></span> </label> <label for="id_beatles_1"> Paul <span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" /></span> </label> <label for="id_beatles_2"> George <span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" /></span> </label> <label for="id_beatles_3"> Ringo <span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" /></span> </label>Если вы не будете перебирать радиокнопки – например, если в вашем шаблоне просто включён
{{ myform.beatles }}– они будут выведены в<ul>с тегами<li>, как показано выше.Внешний контейнер
<ul>получает атрибутidвиджета, если он определён, илиBoundField.auto_idв противном случае.При переборе радиокнопок теги
labelиinputсодержат атрибутыforиidсоответственно. У каждой радиокнопки есть атрибутid_for_labelдля вывода ID элемента.
CheckboxSelectMultiple
-
class CheckboxSelectMultiple[source] -
Аналогично
SelectMultiple, но отображается как список флажков:<ul> <li><input type='checkbox' name='...' ></li> ... </ul>
Внешний контейнер
<ul>получает атрибутidвиджета, если он определён, илиBoundField.auto_idв противном случае.
Как и в случае с RadioSelect, теперь можно перебирать отдельные флажки, составляющие списки. Подробности см. в документации RadioSelect.
При переборе флажков теги label и input содержат атрибуты for и id соответственно. У каждого флажка есть атрибут id_for_label для вывода ID элемента.
Виджеты загрузки файлов
FileInput
-
class FileInput[source] -
Ввод для загрузки файлов:
<input type='file' ...>
ClearableFileInput
-
class ClearableFileInput[source] -
Ввод для загрузки файлов:
<input type='file' ...>, с дополнительным флажком для очистки значения поля, если поле не обязательно и имеет начальные данные.
Составные виджеты
MultipleHiddenInput
-
class MultipleHiddenInput[source] -
Множественные
<input type='hidden' ...>виджеты.Виджет, обрабатывающий несколько скрытых виджетов для полей, имеющих список значений.
-
choices -
Этот атрибут является необязательным, когда поле формы не имеет атрибута
choices. Если он есть, он переопределит всё, что вы задали здесь, когда атрибут обновляется вField.
-
SplitDateTimeWidget
-
class SplitDateTimeWidget[source] -
Обёртка (использующая
MultiWidget) вокруг двух виджетов:DateInputдля даты иTimeInputдля времени. Должен использоваться сSplitDateTimeField, а не сDateTimeField.Этот виджет имеет два необязательных атрибута:
-
date_format -
Аналогично
DateInput.format
-
time_format -
Аналогично
TimeInput.format
-
SplitHiddenDateTimeWidget
-
class SplitHiddenDateTimeWidget[source] -
Аналогично
SplitDateTimeWidget, но используетHiddenInputдля даты и времени.
SelectDateWidget
-
class SelectDateWidget[source] -
Обёртка вокруг трёх
Selectвиджетов: один для месяца, дня и года.Принимает несколько необязательных аргументов:
-
years -
Необязательный список/кортеж годов для использования в выпадающем списке «год». По умолчанию используется список, содержащий текущий год и следующие 9 лет.
-
months -
Необязательный словарь месяцев для использования в выпадающем списке «месяцы».
Ключи словаря соответствуют номеру месяца (индексируется с 1), а значения — отображаемым месяцам:
MONTHS = { 1:_('jan'), 2:_('feb'), 3:_('mar'), 4:_('apr'), 5:_('may'), 6:_('jun'), 7:_('jul'), 8:_('aug'), 9:_('sep'), 10:_('oct'), 11:_('nov'), 12:_('dec') }
-
empty_label -
Если
DateFieldне является обязательным,SelectDateWidgetбудет иметь пустой выбор вверху списка (по умолчанию это---). Вы можете изменить текст этой метки с помощью атрибутаempty_label.empty_labelможет бытьstring,list, илиtuple. Если используется строка, все выпадающие списки будут иметь пустой выбор с этой меткой. Еслиempty_labelявляетсяlistилиtupleиз 3 строковых элементов, выпадающие списки будут иметь свои собственные пользовательские метки. Метки должны быть в таком порядке('year_label', 'month_label', 'day_label').# A custom empty label with string field1 = forms.DateField(widget=SelectDateWidget(empty_label="Nothing")) # A custom empty label with tuple field1 = forms.DateField( widget=SelectDateWidget( empty_label=("Choose Year", "Choose Month", "Choose Day"), ), )
Этот виджет раньше находился в пакете
django.forms.extras.widgets. Сейчас он определён вdjango.forms.widgetsи, как и другие виджеты, его можно импортировать напрямую изdjango.forms. -
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.9/ref/forms/widgets/