Spec-Zone.ru › Django 1.10

Виджеты

Виджет — это представление 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-списка, в то время как 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" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required /></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" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40" required /></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" required />'

Если вы присваиваете значение 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.

Добавлено в Django 1.9:

В более старых версиях этот атрибут был определен только для виджетов даты и времени (как False).

format_value(value)

Очищает и возвращает значение для использования в шаблоне виджета. value не гарантируется, что будет допустимым входом, поэтому реализации подклассов должны быть защищены от возможных ошибок.

Изменено в Django 1.10:

В более ранних версиях этот метод был закрытым API с именем _format_value(). Старое имя будет работать до Django 2.0.

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 может быть вызван более одного раза во время обработки данных формы, поэтому, если вы его настраиваете и добавляете дорогостоящие операции, вам нужно реализовать собственный механизм кэширования.

value_omitted_from_data(data, files, name) [source]
Новое в Django 1.10.2.

Исходя из словарей data и files и имени этого виджета, возвращает, есть ли данные или файлы для виджета.

Результат метода влияет на то, будет ли поле в форме модели возвращаться к своему значению по умолчанию.

Особые случаи – CheckboxInput, CheckboxSelectMultiple и SelectMultiple, которые всегда возвращают False , так как не отмеченный флажок и не выбранный <select multiple> не появляются в данных при отправке HTML-формы, поэтому неизвестно, предоставил ли пользователь значение.

use_required_attribute(initial) [source]
Новое в Django 1.10.1.

Принимая значение поля формы initial , возвращает, можно ли отобразить виджет с атрибутом HTML required . Формы используют этот метод вместе с Field.required и Form.use_required_attribute для определения того, нужно ли отображать атрибут required для каждого поля.

По умолчанию возвращает False для скрытых виджетов и True в противном случае. Особые случаи – ClearableFileInput, который возвращает False когда initial не задано, и CheckboxSelectMultiple, который всегда возвращает False , так как проверка браузера потребовала бы проверки всех флажков вместо хотя бы одного.

Переопределите этот метод в пользовательских виджетах, которые не совместимы с проверкой браузера. Например, виджет WYSIWYG текстового редактора, поддерживаемый скрытым элементом textarea , может всегда возвращать False , чтобы избежать проверки браузера на скрытом поле.

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 используемый при рендеринге может быть одним из двух:

  • Список.
  • Единственное значение (например, строка), представляющее «скомпрессированное» представление набора значений.

Если 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, учитывая Локализация формата.

По умолчанию, микросекундная часть значения времени всегда устанавливается в 0. Если микросекунды необходимы, используйте подкласс с атрибутом supports_microseconds, установленным в True.

TimeInput

class TimeInput [source]

Ввод времени в виде простого текстового поля: <input type='text' ...>

Принимает те же аргументы, что и TextInput, плюс один необязательный аргумент:

format

Формат, в котором будет отображено начальное значение этого поля.

Если аргумент format не указан, используется первый формат из TIME_INPUT_FORMATS, учитывая Локализация формата.

Для обработки микросекунд см. DateTimeInput.

Textarea

class Textarea [source]

Область ввода текста: <textarea>...</textarea>

Виджеты выбора и флажков

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" required /> John</label>
</div>
<div class="myradio">
    <label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /> Paul</label>
</div>
<div class="myradio">
    <label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /> George</label>
</div>
<div class="myradio">
    <label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /> 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" required /></span>
</label>

<label for="id_beatles_1">
    Paul
    <span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /></span>
</label>

<label for="id_beatles_2">
    George
    <span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /></span>
</label>

<label for="id_beatles_3">
    Ringo
    <span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /></span>
</label>

Если вы решили не перебирать радиокнопки — например, если ваш шаблон просто включает {{ myform.beatles }} — они будут выведены в <ul> с <li> тегами, как и выше.

Внешний контейнер <ul> получает атрибут id виджета, если он определен, или BoundField.auto_id в противном случае.

При переборе радиокнопок теги label и input включают for и id атрибуты соответственно. Каждая радиокнопка имеет атрибут id_for_label для вывода идентификатора элемента.

CheckboxSelectMultiple

class CheckboxSelectMultiple [source]

Аналогично SelectMultiple, но отображается как список флажков:

<ul>
  <li><input type='checkbox' name='...' ></li>
  ...
</ul>

Внутренний контейнер <ul> получает атрибут id виджета, если он определён, или BoundField.auto_id в противном случае.

Как и в RadioSelect, вы можете перебирать отдельные флажки для значений виджета. В отличие от RadioSelect, флажки не будут включать required HTML-атрибут, если поле является обязательным, так как проверка браузера потребует, чтобы все флажки были отмечены, а не хотя бы один.

При переборе флажков, теги 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 для времени. Должен использоваться с SplitDateTimeField, а не с DateTimeField.

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 1.9:

Этот виджет раньше находился в пакете 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.10/ref/forms/widgets/

Spec-Zone.ru

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