部件

    内置部件生成的 HTML 使用 HTML5 语法,目标是 <!DOCTYPE html>。例如,它使用布尔属性,如 checked 而不是 XHTML 风格的 checked='checked'

    小技巧

    部件不应该与 混淆。表单字段处理输入验证的逻辑,直接在模板中使用。小组件处理 HTML 表单输入元素在网页上的渲染和原始提交数据的提取。然而,部件确实需要 分配 到表单字段。

    每当你在表单中指定一个字段时,Django 会使用一个默认的部件来显示数据类型。要想知道哪个字段使用的是哪个部件,请看 的文档。

    但是,如果你想为一个字段使用不同的部件,你可以在字段定义中使用 widget 参数。例如:

    这将指定一个带有注释的表单,该表单使用一个较大的 部件,而不是默认的 TextInput 部件。

    为部件设置参数

    许多部件都有可选的额外参数;它们可以在字段上定义部件时进行设置。在下面的例子中, 属性被设置为 SelectDateWidget

    1. from django import forms
    2. BIRTH_YEAR_CHOICES = ['1980', '1981', '1982']
    3. FAVORITE_COLORS_CHOICES = [
    4. ('blue', 'Blue'),
    5. ('green', 'Green'),
    6. ('black', 'Black'),
    7. ]
    8. class SimpleForm(forms.Form):
    9. birth_year = forms.DateField(widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICES))
    10. favorite_colors = forms.MultipleChoiceField(
    11. required=False,
    12. widget=forms.CheckboxSelectMultiple,
    13. choices=FAVORITE_COLORS_CHOICES,
    14. )

    请参阅 ,了解更多关于哪些部件可用以及它们接受哪些参数的信息。

    继承自 Select 部件的部件处理选择。它们向用户提供了一个可供选择的选项列表。不同的部件以不同的方式呈现这种选择; 部件本身使用 <select> HTML 列表表示,而 RadioSelect 使用单选按钮。

    部件默认用于 ChoiceField 字段。部件上显示的选择是继承自 ,改变 ChoiceField.options 将更新 Select.options。例如:

    1. >>> from django import forms
    2. >>> CHOICES = [('1', 'First'), ('2', 'Second')]
    3. >>> choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES)
    4. >>> choice_field.choices
    5. [('1', 'First'), ('2', 'Second')]
    6. >>> choice_field.widget.choices
    7. [('1', 'First'), ('2', 'Second')]
    8. >>> choice_field.widget.choices = []
    9. >>> choice_field.choices = [('1', 'First and only')]
    10. >>> choice_field.widget.choices
    11. [('1', 'First and only')]

    然而,提供 chips 属性的部件可以与非基于选择的字段一起使用——例如 CharField——但当选择是模型固有的,而不仅仅是表示部件时,建议使用 为基础的字段。

    自定义部件实例

    当 Django 将一个部件渲染成 HTML 时,它只渲染非常少的标记——Django 不会添加类名,或者任何其他部件特定的属性。这意味着,例如,所有 TextInput 部件将在你的网页上显示相同的内容。

    有两种方法来定制部件: 和 每个部件类

    如果你想让一个部件实例看起来与另一个不同,你需要在实例化部件对象并将其分配给表单字段时指定额外的属性(也许还需要在你的 CSS 文件中添加一些规则)。

    例如,采取以下表单:

    1. from django import forms
    2. class CommentForm(forms.Form):
    3. name = forms.CharField()
    4. url = forms.URLField()
    5. comment = forms.CharField()

    这个表单将包含三个默认的 部件,具有默认的渲染——没有 CSS 类,没有额外的属性。这意味着为每个小组件提供的输入框将被完全渲染:

    1. >>> f = CommentForm(auto_id=False)
    2. >>> f.as_table()
    3. <tr><th>Name:</th><td><input type="text" name="name" required></td></tr>
    4. <tr><th>Url:</th><td><input type="url" name="url" required></td></tr>
    5. <tr><th>Comment:</th><td><input type="text" name="comment" required></td></tr>

    在真实的网页上,你可能不希望每个小组件看起来都一样。你可能希望评论的输入元素更大,你可能希望“名称”部件有一些特殊的 CSS 类。你也可以指定“type”属性,以利用新的 HTML5 输入类型。 要做到这一点,你可以在创建部件时使用 Widget.attrs 参数:

    1. class CommentForm(forms.Form):
    2. name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
    3. url = forms.URLField()
    4. comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))

    你也可以在表单定义中修改一个部件:

    1. class CommentForm(forms.Form):
    2. name = forms.CharField()
    3. url = forms.URLField()
    4. comment = forms.CharField()
    5. name.widget.attrs.update({'class': 'special'})
    6. comment.widget.attrs.update(size='40')

    或者如果该字段没有直接在表单上声明(比如模型表单字段),可以使用 属性:

    1. class CommentForm(forms.ModelForm):
    2. def __init__(self, *args, **kwargs):
    3. super().__init__(*args, **kwargs)
    4. self.fields['name'].widget.attrs.update({'class': 'special'})
    5. self.fields['comment'].widget.attrs.update(size='40')

    Django 会将额外的属性包含在渲染的输出中:

    你也可以使用 attrs 设置 HTML id。参见 的例子。

    样式化部件类

    有了部件,就可以添加静态资源(cssjavascript)并更深入地定制它们的外观和行为。

    简而言之,你需要对部件进行子类化,并且 定义一个内部“Media”类 或者 。

    这些方法涉及到一些高级的 Python 编程,在 表单静态资源 主题指南中有详细描述。

    基础部件类 和 MultiWidget 被所有的 子类化,可以作为自定义部件的基础。

    class Widget(attrs=None)

    这个抽象类不能被渲染,但提供了基本属性 attrs。 你也可以在自定义部件上实现或重写 方法。

    • attrs

      包含要在渲染的部件上设置的 HTML 属性的字典。

      1. >>> from django import forms
      2. >>> name = forms.TextInput(attrs={'size': 10, 'title': 'Your name'})
      3. >>> name.render('name', 'A name')
      4. '<input title="Your name" type="text" name="name" value="A name" size="10">'

      如果你给一个属性分配一个 TrueFalse 的值,它将被渲染成一个 HTML5 布尔属性:

      1. >>> name = forms.TextInput(attrs={'required': True})
      2. '<input name="name" type="text" value="A name" required>'
      3. >>>
      4. >>> name = forms.TextInput(attrs={'required': False})
      5. >>> name.render('name', 'A name')
      6. '<input name="name" type="text" value="A name">'
    • supports_microseconds

      属性,默认为 True。如果设置为 False,则 datetime 和 值的微秒部分将被设置为 0

    • format_value(value)

      清理并返回一个值,供部件模板使用。value 并不能保证是有效的输入,因此子类的实现应该是防御性的。

    • get_context(name, value, attrs)

      返回渲染部件模板时要使用的值的字典。默认情况下,该字典包含一个键,'widget',它是一个包含以下键的部件的字典表示:

      • 'name'name 参数中的字段名称。
      • 'is_hidden':表示该部件是否被隐藏的布尔值。
      • 'required':表示该部件是否需要该字段的布尔值。
      • 'value'::meth:`format_value’ 返回的值。
      • 'attrs':拟在渲染的部件上设置的 HTML 属性。 属性和 attrs 参数的组合。
      • ''template_name'self.template_name 的值。

      Widget 子类可以通过覆盖该方法提供自定义上下文值。

    • id_for_label(id_)

      返回该部件的 HTML ID 属性,供 <label>,给定字段的 ID 使用。如果 ID 不可用,则返回 None

      这个钩子是必要的,因为一些部件有多个 HTML 元素,因此有多个 ID。在这种情况下,这个方法应该返回一个与部件标签中第一个 ID 对应的 ID 值。

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

      使用给定的渲染器将部件渲染成 HTML。如果 rendererNone,则使用 FORM_RENDERER 设置中的渲染器。

    • value_from_datadict(data, files, name)

      给定一个数据字典和这个部件的名称,返回这个部件的值。files 可能包含来自 的数据。如果没有提供值,则返回 None。还需要注意的是,在处理表单数据的过程中,value_from_datadict 可能会被调用不止一次,所以如果你自定义它并添加昂贵的处理,你应该自己实现一些缓存机制。

    • value_omitted_from_data(data, files, name)

      给定 datafiles 字典和这个部件的名称,返回该部件是否有数据或文件。

      该方法的结果会影响模型表单中的字段 是否回到默认

      特殊情况有 、CheckboxSelectMultiple 和 ,它们总是返回 False,因为未选中的复选框和未选择的 <select multiple>,不会出现在 HTML 表单提交的数据中,所以不知道用户是否提交了一个值。

    • use_required_attribute(initial)

      给定一个表单字段的 initial 值,返回是否可以用 required HTML 属性来渲染部件。表单使用这个方法与 Field.requiredForm.use_required_attribute 一起决定是否为每个字段显示 required 属性。

      默认情况下,对隐藏的部件返回 False,否则返回 True。特殊情况是 和 ClearableFileInput,当设置了 initial 时,返回 False;还有 ,总是返回 False,因为浏览器验证需要选中所有的复选框,而不是至少一个。

      在与浏览器验证不兼容的自定义部件中覆盖此方法。例如,一个由隐藏的 textarea 元素支持的 WSYSIWG 文本编辑部件可能希望总是返回 False 以避免浏览器对隐藏字段的验证。

      Changed in Django 3.1:

    MultiWidget

    class MultiWidget(widgets, attrs=None)

    与 携手合作。

    MultiWidget 有一个必要的参数:

    • widgets

      一个包含所需部件的迭代器。例如:

      1. >>> from django.forms import MultiWidget, TextInput
      2. >>> widget = MultiWidget(widgets=[TextInput, TextInput])
      3. >>> widget.render('name', ['john', 'paul'])
      4. '<input type="text" name="name_0" value="john"><input type="text" name="name_1" value="paul">'

      你可以提供一个字典,以便为每个子部件的 name 属性指定自定义后缀。在这种情况下,对于每个 (key, widget) 对,键将被附加到部件的 name 上,以便生成属性值。你可以为单个键提供空字符串(''),以便为一个部件压制后缀。例如:

      1. >>> widget = MultiWidget(widgets={'': TextInput, 'last': TextInput})
      2. >>> widget.render('name', ['john', 'paul'])
      3. '<input type="text" name="name" value="john"><input type="text" name="name_last" value="paul">'

    还有一个必要的方法:

    • decompress(value)

      这个方法从字段中获取一个“压缩”值,然后返回一个“解压缩”值的列表。可以假定输入值有效,但不一定是非空的。

      这个方法 必须由子类实现,由于值可能是空的,所以实现必须是防御性的。

      “解压”背后的原理是,需要将表单字段的组合值“拆分”成每个部件的值。

      一个例子是 如何将一个 datetime 值变成一个列表,将日期和时间分成两个独立的值:

      1. from django.forms import MultiWidget
      2. class SplitDateTimeWidget(MultiWidget):
      3. # ...
      4. def decompress(self, value):
      5. if value:
      6. return [value.date(), value.time()]
      7. return [None, None]

      小技巧

      请注意 有一个补充方法 compress(),其职责与之相反——将所有成员字段的清理值合并为一个。

    它提供了一些自定义上下文:

    • get_context(name, value, attrs)

      In addition to the 'widget' key described in , MultiWidget adds a widget['subwidgets'] key.

      这些可以在部件模板中循环使用:

      1. {% for subwidget in widget.subwidgets %}
      2. {% include subwidget.template_name with widget=subwidget %}
      3. {% endfor %}

    下面是一个例子,它子类为 MultiWidget,用于在不同的选择框中显示日期和年、月、日。这个部件的目的是与 而不是 MultiValueField 一起使用,因此我们实现了 :

    1. from datetime import date
    2. from django import forms
    3. class DateSelectorWidget(forms.MultiWidget):
    4. def __init__(self, attrs=None):
    5. days = [(day, day) for day in range(1, 32)]
    6. months = [(month, month) for month in range(1, 13)]
    7. years = [(year, year) for year in [2018, 2019, 2020]]
    8. widgets = [
    9. forms.Select(attrs=attrs, choices=days),
    10. forms.Select(attrs=attrs, choices=months),
    11. forms.Select(attrs=attrs, choices=years),
    12. ]
    13. super().__init__(widgets, attrs)
    14. def decompress(self, value):
    15. if isinstance(value, date):
    16. return [value.day, value.month, value.year]
    17. elif isinstance(value, str):
    18. year, month, day = value.split('-')
    19. return [day, month, year]
    20. return [None, None, None]
    21. def value_from_datadict(self, data, files, name):
    22. day, month, year = super().value_from_datadict(data, files, name)
    23. # DateField expects a single string that it can parse into a date.
    24. return '{}-{}-{}'.format(year, month, day)

    构造函数在一个列表中创建了多个 Select 部件。super() 方法使用这个列表来设置部件。

    所需的方法 将一个 datetime.date 的值分解成对应于每个部件的日、月、年的值。如果选择了一个无效的日期,比如不存在的 2 月 30 日,那么 DateField 就会把这个方法传给一个字符串代替,所以需要进行解析。最后的 return 处理的是 valueNone 的时候,也就是说我们的子部件没有任何默认值。

    的默认实现是返回一个与每个 Widget 对应的值列表。这在使用 MultiWidgetMultiValueField` 时是合适的。但由于我们想将这个部件与一个 DateField 一起使用,它只取一个值,我们已经覆盖了这个方法。这里的实现将来自子部件的数据组合成一个字符串,其格式为 所期望的格式。

    内置部件

    Django 在 django.forms.widgets 模块中提供了所有基本的 HTML 部件,以及一些常用的部件组,包括 文本输入、、上传文件 和 。

    这些部件使用了 HTML 元素 inputtextarea

    TextInput

    class TextInput

    • input_type'text'
    • template_name'django/forms/widgets/text.html'
    • 渲染为:<input type="text" ...>

    NumberInput

    class NumberInput

    • input_type'number'
    • template_name'django/forms/widgets/number.html'
    • 渲染为:<input type="number" ...>

    请注意,并不是所有的浏览器都支持在 number 输入类型中输入本地化的数字。Django 本身就避免在 localize 属性设置为 True 的字段中使用它们。

    EmailInput

    class EmailInput

    • input_type'email'
    • template_name'django/forms/widgets/email.html'
    • 渲染为:<input type="email" ...>

    URLInput

    class URLInput

    • input_type'url'
    • template_name'django/forms/widgets/url.html'
    • 渲染为:<input type="url" ...>

    PasswordInput

    class PasswordInput

    • input_type'password'
    • template_name'django/forms/widgets/password.html'
    • 渲染为:<input type="password" ...>

    需要一个可选的参数:

    • render_value

      确定当验证错误后重新显示表格时,部件是否会有一个值被填入(默认为 False)。

    HiddenInput

    class HiddenInput

    • input_type'hidden'
    • template_name'django/forms/widgets/hidden.html'
    • 渲染为:<input type="hidden" ...>

    请注意,还有一个 部件,封装了一组隐藏的输入元素。

    DateInput

    class DateInput

    • input_type'text'
    • template_name'django/forms/widgets/date.html'
    • 渲染为:<input type="text" ...>

    采用与 TextInput 相同的参数,多一个可选参数:

    • format

      显示该字段初始值的格式。

    如果没有提供 format 参数,默认的格式是 中找到的第一种格式,并且尊重 本地格式化

    DateTimeInput

    class DateTimeInput

    • input_type'text'
    • template_name'django/forms/widgets/datetime.html'
    • 渲染为:<input type="text" ...>

    采用与 相同的参数,多一个可选参数:

    • format

      显示该字段初始值的格式。

    如果没有提供 format 参数,默认的格式是 DATETIME_INPUT_FORMATS 中找到的第一种格式,并且尊重 。

    默认情况下,时间值的微秒部分总是设置为 0。如果需要微秒,则使用 supports_microseconds 属性设置为 True 的子类。

    TimeInput

    class TimeInput

    • template_name'django/forms/widgets/time.html'
    • 渲染为:<input type="text" ...>

    采用与 相同的参数,多一个可选参数:

    • format

      显示该字段初始值的格式。

    如果没有提供 format 参数,默认的格式是 TIME_INPUT_FORMATS 中找到的第一种格式,并且尊重 。

    关于微秒的处理,请参见 DateTimeInput

    Textarea

    class Textarea

    • template_name'django/forms/widgets/textarea.html'
    • 渲染为:<textarea>...</textarea>

    选择器和复选框部件

    呈现多个选择的部件有一个 option_template_name 属性,指定用于渲染每个选择的模板。例如,对于 部件,select_option.html 会为 <select> 渲染 <option>

    CheckboxInput

    class CheckboxInput

    • input_type'checkbox'
    • template_name'django/forms/widgets/checkbox.html'
    • 渲染为:<input type="checkbox" ...>

    需要一个可选的参数:

    • 一个可调用对象,它接受 CheckboxInput 的值,并返回 True,如果该复选框应检查该值。

    Select

    class Select

    • template_name'django/forms/widgets/select.html'
    • option_template_name'django/forms/widgets/select_option.html'
    • 渲染为:<select><option ...>...</select>

    • choices

      当表单字段没有 choices 属性时,这个属性是可选的。如果有,当 Field 属性更新时,它将覆盖你在这里设置的任何属性。

    NullBooleanSelect

    class NullBooleanSelect

    • template_name'django/forms/widgets/select.html'
    • option_template_name'django/forms/widgets/select_option.html'

    选择“未知”、“是”和“否”选项的小组件。

    SelectMultiple

    class SelectMultiple

    • template_name'django/forms/widgets/select.html'
    • option_template_name'django/forms/widgets/select_option.html'

    类似于 ,但允许多选:<select multiple>...</select>

    RadioSelect

    class RadioSelect

    • template_name'django/forms/widgets/radio.html'
    • option_template_name'django/forms/widgets/radio_option.html'

    类似于 Select,但在 <li> 标签中呈现为单选按钮列表:

    为了对生成的标记进行更精细的控制,你可以在模板中循环使用单选按钮。假设一个表单 myform 有一个字段 beatles,使用 RadioSelect 作为它的部件。

    1. {% for radio in myform.beatles %}
    2. <div class="myradio">
    3. {{ radio }}
    4. </div>
    5. {% endfor %}

    这将产生以下 HTML:

    1. <div class="myradio">
    2. <label for="id_beatles_0"><input id="id_beatles_0" name="beatles" type="radio" value="john" required> John</label>
    3. </div>
    4. <div class="myradio">
    5. <label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required> Paul</label>
    6. </div>
    7. <div class="myradio">
    8. <label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" required> George</label>
    9. </div>
    10. <div class="myradio">
    11. <label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required> Ringo</label>
    12. </div>

    这包括 <label> 标签。为了得到更多的细节,你可以使用每个单选按钮的 tagchoice_labelid_for_label 属性。例如,这个模板…

    1. {% for radio in myform.beatles %}
    2. <label for="{{ radio.id_for_label }}">
    3. {{ radio.choice_label }}
    4. <span class="radio">{{ radio.tag }}</span>
    5. </label>
    6. {% endfor %}

    …将导致以下 HTML:

    1. <label for="id_beatles_0">
    2. John
    3. <span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" required></span>
    4. </label>
    5. <label for="id_beatles_1">
    6. Paul
    7. <span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required></span>
    8. </label>
    9. <label for="id_beatles_2">
    10. George
    11. <span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" required></span>
    12. </label>
    13. <label for="id_beatles_3">
    14. Ringo
    15. <span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required></span>
    16. </label>

    如果你决定不在单选按钮上循环——例如,如果你的模板中包含 { myform.beatles }}——它们将被输出到一个带有 <li> 标签的 <ul> 中,如上。

    外层的 <ul> 容器接收部件的 id 属性,如果定义了,否则 。

    在循环单选按钮时,labelinput 标签分别包含 forid 属性。每个单选按钮都有一个 id_for_label 属性来输出元素的 ID。

    CheckboxSelectMultiple

    class CheckboxSelectMultiple

    • template_name'django/forms/widgets/checkbox_select.html'
    • option_template_name'django/forms/widgets/checkbox_option.html'

    类似于 SelectMultiple,但渲染为一个复选框列表。

    1. <ul>
    2. <li><input type="checkbox" name="..." ></li>
    3. ...
    4. </ul>

    外层的 <ul> 容器接收部件的 id 属性,如果定义了,否则 。

    RadioSelect 一样,你可以循环使用各个复选框来进行部件的选择。与 不同的是,如果字段是必填的,则复选框不会包含 required HTML 属性,因为浏览器验证会要求选中所有复选框,而不是至少一个。

    在循环复选框时,labelinput 标签分别包含 forid 属性。每个复选框都有一个 id_for_label 属性来输出元素的 ID。

    FileInput

    class FileInput

    • template_name'django/forms/widgets/file.html'
    • 渲染为:<input type="file" ...>

    ClearableFileInput

    class ClearableFileInput

    • template_name'django/forms/widgets/clearable_file_input.html'
    • 渲染为 <input type="file" ...>,如果该字段不需要且有初始数据,则增加一个复选框输入,以清除该字段的值。

    复合部件

    MultipleHiddenInput

    class MultipleHiddenInput

    • template_name'django/forms/widgets/multiple_hidden.html'
    • 渲染为:多个 <input type="hidden" ...> 标签

    一个处理具有值列表的字段的多个隐藏部件。

    SplitDateTimeWidget

    class SplitDateTimeWidget

    • template_name'django/forms/widgets/splitdatetime.html'

    围绕两个小组件的封装器(使用 MultiWidget): 代表日期, TimeInput 代表时间。必须使用 而不是 DateTimeField

    SplitDateTimeWidget 有几个可选参数:

    • date_format

      类似于

    • time_format

      类似于 TimeInput.format

    • date_attrs

    • time_attrs

      类似于 。一个包含 HTML 属性的字典,要分别在渲染的 DateInput 和 部件上设置。如果没有设置这些属性,则使用 Widget.attrs 代替。

    SplitHiddenDateTimeWidget

    class SplitHiddenDateTimeWidget

    • template_name'django/forms/widgets/splithiddendatetime.html'

    类似于 ,但对日期和时间使用 HiddenInput

    SelectDateWidget

    class SelectDateWidget

    • template_name'django/forms/widgets/select_date.html'

    围绕三个 部件的封装器:月、日、年各一个。

    需要几个可选的参数:

    • years

      在“年份”选择框中使用的可选年份列表/年份组。默认值是包含当前年份和未来 9 年的列表。

    • months

      在“月份”选择框中可选择使用的月份。

      字典的键与月数相对应(1 开头索引),其值是显示的月份:

      1. MONTHS = {
      2. 1:_('jan'), 2:_('feb'), 3:_('mar'), 4:_('apr'),
      3. 5:_('may'), 6:_('jun'), 7:_('jul'), 8:_('aug'),
      4. 9:_('sep'), 10:_('oct'), 11:_('nov'), 12:_('dec')
      5. }
    • empty_label

      如果 DateField 不是必需的, 将在列表顶部有一个空的选择(默认是 --`` )。你可以通过 empty_label 属性来改变这个标签的文本。empty_label 可以是 stringlist 或者 tuple。当使用字符串时,所有的选择框都会有一个带这个标签的空选择。如果 empty_label 是一个由 3 个字符串元素组成的 listtuple,选择框将有自己的自定义标签。标签的顺序应该是 ('year_label', 'month_label', 'day_label')

      1. # A custom empty label with string
      2. field1 = forms.DateField(widget=SelectDateWidget(empty_label="Nothing"))
      3. # A custom empty label with tuple
      4. field1 = forms.DateField(
      5. widget=SelectDateWidget(
      6. empty_label=("Choose Year", "Choose Month", "Choose Day"),
      7. ),