it-swarm.com.ru

Django: Как создать собственный виджет формы?

Мне трудно найти документацию о том, как написать собственный виджет.

Мои вопросы:

  • Если я создаю собственный виджет, можно ли его использовать одинаково для интерфейса администратора или для обычных форм?
  • Если я хочу разрешить пользователю редактировать список элементов, какой виджет я должен подкласс? Какие методы виджета мне нужно переопределить/реализовать?
  • Какой метод виджета отвечает за переход от ввода пользователя обратно к модели данных?

Благодарю.

51
Nick Heiner

Вы правы в том, что Django не предоставляет документацию по этой конкретной теме. Я советую вам взглянуть на встроенные виджеты в Django.forms.widgets (я буду ссылаться на классы из этого модуля ниже).

Если я создаю собственный виджет, можно ли его использовать одинаково для интерфейса администратора или для обычных форм?

Администратор переопределяет некоторые виджеты (см. Django.contrib.admin.options.FORMFIELD_FOR_DBFIELD_DEFAULTS ). Возможно, вы можете создать подкласс ModelAdmin и изменить атрибут formfield_overrides, но я никогда ничего не делал с ModelAdmin, поэтому я не могу здесь помочь ...

Если я хочу разрешить пользователю редактировать список элементов, какой виджет я должен подкласс? Какие методы виджета мне нужно переопределить/реализовать?

Вероятно, ваш виджет не имеет ничего общего с виджетами по умолчанию (с Select, если есть ?!). Подкласс от Widget и если вы найдете какой-либо общий шаблон со встроенными, вы все равно можете изменить его позже.

Реализуйте следующие методы:

  • render(self, name, value, attrs=None, renderer=None)

    Проверьте Input.render для простого примера. Он также поддерживает пользовательские атрибуты, которые включены в HTML. Вы также можете добавить атрибуты "id", смотрите MultipleHiddenInput.render о том, как это сделать. Не забудьте использовать mark_safe при выводе HTML напрямую. Если у вас довольно сложный виджет, вы можете использовать рендеринг шаблона ( пример ).

  • _has_changed(self, initial, data)

    Необязательный. Используется в admin для регистрации сообщений о том, что было изменено.

Какой метод виджета отвечает за переход от ввода пользователя обратно к модели данных?

Это не имеет ничего общего с виджетом - Django не может знать, какой виджет использовался в предыдущем запросе. Он может использовать только данные формы (POST), отправленные из формы. Следовательно, метод поля Field.to_python используется для преобразования ввода в тип данных Python (может вызвать ValidationError, если ввод недопустим).

46
AndiDog

Джанго <1.11

В дополнение к другим ответам, это небольшой пример кода пользовательского виджета:

widgets.py:

from Django.forms.widgets import Widget
from Django.template import loader
from Django.utils.safestring import mark_safe


class MyWidget(Widget):
    template_name = 'myapp/my_widget.html'

    def get_context(self, name, value, attrs=None):
        return {'widget': {
            'name': name,
            'value': value,
        }}

    def render(self, name, value, attrs=None):
        context = self.get_context(name, value, attrs)
        template = loader.get_template(self.template_name).render(context)
        return mark_safe(template)

my_widget.html:

<textarea id="mywidget-{{ widget.name }}" name="{{ widget.name }}">
{% if widget.value %}{{ widget.value }}{% endif %}</textarea>

Джанго 1.11

Виджеты теперь обрабатываются с помощью API рендеринга форм .

21
Wtower

ПРИМЕЧАНИЕ: здесь есть три вопроса. Первые два вопроса см. В более полном ответе AndiDog. Я только отвечаю на третий вопрос здесь:

В. Какой метод виджета отвечает за переход от ввода пользователя обратно к модели данных?

A. Метод value_from_datadict - это своего рода обратное методу render виджета. Этот метод, вероятно, является тем, на что ссылаются документы Django на виджеты, когда он говорит: «Виджет обрабатывает рендеринг HTML и извлечение данных из словаря GET/POST, который соответствует виджету». В этой документации больше ничего нет, но вы можете увидеть, как это работает, из кода для встроенных виджетов.

5
Ghopper21

Обычно я начинаю с наследования от одного из существующих виджетов, добавляю новое требуемое свойство и затем изменяю метод рендеринга. Вот пример для фильтруемого виджета выбора, который я реализовал. Фильтрация осуществляется с помощью мобильного телефона jquery.

class FilterableSelectWidget(forms.Select):
    def __init__(self, attrs=None, choices=()):
        super(FilterableSelectWidget, self).__init__(attrs, choices)
        # choices can be any iterable, but we may need to render this widget
        # multiple times. Thus, collapse it into a list so it can be consumed
        # more than once.
        self._data_filter = {}

    @property
    def data_filter(self):
        return self._data_filter

    @data_filter.setter
    def data_filter(self, attr_dict):
        self._data_filter.update(attr_dict)

    def render_option(self, selected_choices, option_value, option_label):
        option_value = force_text(option_value)
        if option_value in selected_choices:
            selected_html = mark_safe(' selected="selected"')
            if not self.allow_multiple_selected:
                # Only allow for a single selection.
                selected_choices.remove(option_value)
        else:
            selected_html = ''
        # use self.data_filter
        filtertext = self.data_filter.get(option_value)
        data_filtertext = 'data-filtertext="{filtertext}"'.\
            format(filtertext=filtertext) if filtertext else ''
        return format_html('<option value="{0}"{1} {3}>{2}</option>',
                           option_value,
                           selected_html,
                           force_text(option_label),
                           mark_safe(data_filtertext))

Затем в представлениях, где я создаю форму, я установлю data_filter для поля.

        some_form.fields["some_field"] = \
            forms.ChoiceField(choices=choices,
                              widget=FilterableSelectWidget)
        some_form.fields["some_field"].widget.data_filter = \
            data_filter
3
Al Conrad

Документация на сайте Django совсем не помогает. Это предложения по настройке виджетов, здесь , прерывают использование form.as_p(), которая затем ставит под угрозу значение форм, как представлено в Django, то есть: сборке виджетов.

Решения, которые мне понравились больше всего - это floppyforms . Это облегчает определение виджетов с помощью шаблонов и является (почти) прозрачной заменой для собственного модуля форм Django. Он имеет отличную документацию и его легко подобрать.

0
Peter Shannon