我很难找到有关如何编写自定义小部件的文档。
我的问题是:
感谢。
答案 0 :(得分:47)
你说得对,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
了解如何执行此操作。在直接输出HTML时不要忘记使用mark_safe
。如果你有一个相当复杂的小部件,你可以使用模板渲染(example)。
_has_changed(self, initial, data)
可选。在admin中用于记录有关更改内容的消息。
哪种小部件方法负责从用户的输入回到数据模型?
这与小部件无关 - Django无法知道先前请求中使用了哪个小部件。它只能使用从表单发送的表单(POST)数据。因此,字段方法Field.to_python
用于将输入转换为Python数据类型(如果输入无效,可能会引发ValidationError
。)
答案 1 :(得分:21)
除了其他答案,这是自定义小部件的一个小代码示例:
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>
现在使用form rendering API呈现窗口小部件。
答案 2 :(得分:5)
注意:这里有三个问题。对于前两个问题,请参阅AndiDog的更全面的答案。我现在只回答第三个问题:
Q值。什么小部件方法负责从用户的输入回到数据模型?
一个。 value_from_datadict
方法 - 它与窗口小部件的render
方法相反。这个方法可能就是小部件上的Django文档所说的“小部件处理HTML的呈现,以及从与小部件对应的GET / POST字典中提取数据”。在文档中没有进一步的内容,但您可以从内置小部件的代码中看到它是如何工作的。
答案 3 :(得分:3)
通常我首先从一个现有的小部件继承,添加一个新的所需属性,然后修改一个render方法。这是我实现的可过滤选择小部件的示例。过滤是通过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
答案 4 :(得分:0)
关于Django网站的文档根本没有帮助。它关于小部件定制的建议,here,打破了form.as_p()
的使用,然后危害了Django中呈现的形式的价值,即:小部件的集合。
我最喜欢的解决方案是floppyforms。它有助于使用模板定义小部件,并且是Django自己的表单模块的(几乎)透明替代品。它具有出色的文档,易于获取。