Spec-Zone.ru › Django 1.8

Виджеты

Виджет — это представление 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/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API