Подтвердить что ты не робот

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

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

Мои вопросы:

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

Спасибо.

4b9b3361

Ответ 1

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

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

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

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

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

Выполните следующие методы:

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

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

  • _has_changed(self, initial, data)

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

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

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

Ответ 2

Django < 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>

Django 1.11

Виджеты теперь отображаются с помощью API визуализации форм.

Ответ 3

ПРИМЕЧАНИЕ. Здесь есть три вопроса. Для первых двух вопросов см. Более полный ответ AndiDog. Я отвечаю только на третий вопрос:

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

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

Ответ 4

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

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

Ответ 5

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

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