Django的:如何build立一个自定义的窗体小部件?

我很难find如何编写自定义小部件的文档。

我的问题是:

  • 如果我构build一个自定义小部件,它可以等同地用于pipe理界面或正常forms?
  • 如果我想允许用户编辑一个项目列表,我应该inheritance哪个小部件? 我需要重写/实现哪些小部件的方法?
  • 什么窗口小部件方法负责从用户的input返回到数据模型?

谢谢。

你说得对,因为Django不提供关于这个特定主题的文档。 我build议你看一下django.forms.widgets中的内置小部件(我将从下面的模块引用类)。

如果我构build一个自定义小部件,它可以等同地用于pipe理界面或正常forms?

pipe理员将覆盖一些小部件(请参阅django.contrib.admin.options.FORMFIELD_FOR_DBFIELD_DEFAULTS )。 您可能可以ModelAdmin子类并更改formfield_overrides属性,但是我从来没有对ModelAdmin做任何事情,所以我无法在这里帮助…

如果我想允许用户编辑一个项目列表,我应该inheritance哪个小部件? 我需要重写/实现哪些小部件的方法?

你的小部件可能与默认的小部件没有任何共同之处(如果有, Select ?!)。 Widget子类,如果你发现任何内置的常见模式,你仍然可以改变它。

实现以下方法:

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

    查看一个简单的例子Input.render 。 它还支持包含在HTML中的用户定义的属性。 您可能还想添加“id”属性,请参阅MultipleHiddenInput.render关于如何做到这一点。 直接输出HTML时不要忘记使用mark_safe 。 如果你有一个相当复杂的部件,你可以使用模板渲染( 例子 )。

  • _has_changed(self, initial, data)

    可选的。 用于pipe理员logging有关更改内容的消息。

什么窗口小部件方法负责从用户的input返回到数据模型?

这与小部件无关 – Django无法知道在之前的请求中使用了哪个小部件。 它只能使用从表单发送的表单(POST)数据。 因此,字段方法Field.to_python用于将input转换为Python数据types(如果input无效,可能会引发ValidationError )。

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呈现 。

注意:这里有三个问题。 对于前两个问题,请参阅AndiDog的完整答案。 我只在这里回答第三个问题:

问:什么窗口小部件方法负责从用户的input返回到数据模型?

A. value_from_datadict方法 – 这是一个小部件render方法的value_from_datadict 。 这个方法大概是Django文档中关于小部件所指的:“小部件处理HTML的呈现,以及从对应于小部件的GET / POST字典中提取数据”。 在文档中没有什么更进一步的内容,但是您可以从内置小部件的代码中看到它是如何工作的。

通常我从一个现有的小部件inheritance,添加一个新的所需属性,然后修改一个渲染方法。 以下是我实现的可筛选select小部件的示例。 过滤是通过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)) 

然后在创build表单的视图中,我将设置该字段的data_filter。

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

Django网站上的文档根本没有帮助。 这是关于小部件定制的build议, 在这里 ,打破form.as_p()的使用,然后危害Django中呈现的forms的价值,即:小部件的集合。

我最喜欢的解决scheme是软盘 。 它有助于使用模板定义小部件,并且是Django自己的表单模块(几乎)透明的替代品。 它具有优秀的文档,很容易拿起。