Django: Как создать виджет пользовательской формы?
Мне сложно найти документацию о том, как писать пользовательский виджет.
Мои вопросы:
- Если я создаю собственный виджет, можно ли его использовать эквивалентно для интерфейса администратора или для нормальных форм?
- Если я хочу разрешить пользователю редактировать список элементов, какой виджет должен быть подклассом? Какие методы виджета мне нужно переопределить/реализовать?
- Какой метод виджета отвечает за переход с пользовательского ввода обратно в модель данных?
Спасибо.
Ответы
Ответ 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. Он имеет отличную документацию и легко подбирается.