Виджеты
Виджет — это представление 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
from django.forms.extras.widgets import SelectDateWidget
BIRTH_YEAR_CHOICES = ('1980', '1981', '1982')
FAVORITE_COLORS_CHOICES = (
('blue', 'Blue'),
('green', 'Green'),
('black', 'Black'),
)
class SimpleForm(forms.Form):
birth_year = forms.DateField(widget=SelectDateWidget(years=BIRTH_YEAR_CHOICES))
favorite_colors = forms.MultipleChoiceField(required=False,
widget=forms.CheckboxSelectMultiple, choices=FAVORITE_COLORS_CHOICES)
См. раздел Встроенные виджеты для получения дополнительной информации о доступных виджетах и принимаемых ими аргументах.
Виджеты, наследующие от виджета Select
Виджеты, наследующие от виджета Select, работают с выбором. Они представляют пользователю список вариантов для выбора. Различные виджеты отображают этот выбор по-разному; виджет Select использует представление HTML-списка, в то время как 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. Также можно указать атрибут «тип», чтобы воспользоваться новыми типами ввода 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 наследуются всеми встроенными виджетами и могут послужить основой для пользовательских виджетов.
-
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" />'
-
render(name, value, attrs=None)[source] -
Возвращает HTML для виджета в виде строки Unicode. Этот метод должен быть реализован подклассом, в противном случае будет поднято исключение
NotImplementedError.Предоставляемое значение ‘value’ не гарантируется, что оно будет правильным вводом, поэтому реализации подклассов должны выполнять защищенное программирование.
-
value_from_datadict(data, files, name)[source] -
Принимая во внимание словарь данных и имя этого виджета, возвращает значение этого виджета.
filesможет содержать данные, полученные изrequest.FILES. ВозвращаетNoneесли значение не было предоставлено. Заметьте также, чтоvalue_from_datadictможет быть вызвано более одного раза во время обработки данных формы, поэтому, если вы его настраиваете и добавляете дорогостоящую обработку, вы должны реализовать собственную механизм кеширования.
-
-
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для отображения даты с днём, месяцем и годом в разных выпадающих списках. Этот виджет предназначен для использования с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, а также учитывается Локализация форматов. -
TimeInput
-
class TimeInput[source] -
Ввод времени в виде простого текстового поля:
<input type='text' ...>Принимает те же аргументы, что и
TextInput, с одним дополнительным необязательным аргументом:-
format -
Формат, в котором будет отображено начальное значение этого поля.
Если аргумент
formatне указан, по умолчанию используется первый формат изTIME_INPUT_FORMATS, а также учитывается Локализация форматов. -
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, определенный в виджете.
При переборе радиокнопок теги label и input содержат атрибуты for и id соответственно. Каждая радиокнопка имеет атрибут id_for_label для вывода идентификатора элемента.
CheckboxSelectMultiple
-
class CheckboxSelectMultiple[source] -
Аналогичен
SelectMultiple, но отображается как список флажков:<ul> <li><input type='checkbox' name='...' ></li> ... </ul>
Внешний контейнер
<ul>получит атрибутid, определенный в виджете.
Как и в RadioSelect, теперь можно перебирать отдельные флажки, составляющие списки. Подробнее см. документацию RadioSelect.
При переборе флажков теги label и input содержат атрибуты for и id соответственно. Каждая галочка имеет атрибут id_for_label для вывода идентификатора элемента.
Виджеты загрузки файлов
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для времени.SplitDateTimeWidgetимеет два необязательных атрибута:-
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 Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/ref/forms/widgets/