WTForms 3.2.2__tar.gz → 3.3.0b2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {wtforms-3.2.2 → wtforms-3.3.0b2}/CHANGES.rst +103 -1
- {wtforms-3.2.2 → wtforms-3.3.0b2}/PKG-INFO +1 -1
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/conf.py +15 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/crash_course.rst +16 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/fields.rst +233 -43
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/forms.rst +3 -3
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/specific_problems.rst +5 -5
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/validators.rst +7 -4
- wtforms-3.3.0b2/docs/widgets.rst +161 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/pyproject.toml +0 -1
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/__init__.py +11 -1
- wtforms-3.3.0b2/src/wtforms/_compat.py +15 -0
- wtforms-3.3.0b2/src/wtforms/datalist.py +122 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/fields/__init__.py +6 -0
- wtforms-3.3.0b2/src/wtforms/fields/choices.py +550 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/fields/core.py +39 -4
- wtforms-3.3.0b2/src/wtforms/fields/datetime.py +293 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/fields/form.py +17 -2
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/fields/list.py +83 -12
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/fields/numeric.py +34 -12
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/fields/simple.py +68 -16
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/form.py +29 -1
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/README.md +6 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.po +8 -8
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.po +8 -9
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.po +9 -8
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/de/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/el/LC_MESSAGES/wtforms.po +7 -7
- wtforms-3.3.0b2/src/wtforms/locale/en/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/en/LC_MESSAGES/wtforms.po +59 -43
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/es/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/et/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.po +6 -6
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.po +8 -9
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/he/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/it/LC_MESSAGES/wtforms.po +10 -10
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.po +7 -8
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.po +9 -11
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.po +9 -10
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.po +11 -11
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.po +8 -9
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.po +5 -6
- wtforms-3.3.0b2/src/wtforms/locale/sr/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/sr/LC_MESSAGES/wtforms.po +53 -48
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.po +6 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/validators.py +223 -30
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/widgets/__init__.py +4 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/widgets/core.py +185 -79
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/conftest.py +18 -4
- wtforms-3.3.0b2/tests/fields/test_button.py +88 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_date.py +9 -0
- wtforms-3.3.0b2/tests/fields/test_datetime.py +116 -0
- wtforms-3.3.0b2/tests/fields/test_datetimelocal.py +141 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_decimal.py +7 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_float.py +13 -2
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_form.py +98 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_html5.py +55 -1
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_integer.py +9 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_list.py +162 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_radio.py +18 -6
- wtforms-3.3.0b2/tests/fields/test_select.py +718 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_selectmultiple.py +112 -14
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_time.py +9 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_week.py +9 -0
- wtforms-3.3.0b2/tests/test_datalist.py +339 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/test_widgets.py +141 -5
- wtforms-3.3.0b2/tests/validators/test_date_range.py +126 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_email.py +47 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_number_range.py +19 -0
- wtforms-3.3.0b2/tests/validators/test_regexp.py +106 -0
- wtforms-3.3.0b2/tests/validators/test_url.py +143 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_uuid.py +16 -1
- wtforms-3.2.2/.hgignore +0 -1
- wtforms-3.2.2/docs/widgets.rst +0 -104
- wtforms-3.2.2/src/wtforms/fields/choices.py +0 -227
- wtforms-3.2.2/src/wtforms/fields/datetime.py +0 -170
- wtforms-3.2.2/src/wtforms/locale/en/LC_MESSAGES/wtforms.mo +0 -0
- wtforms-3.2.2/src/wtforms/locale/sr/LC_MESSAGES/wtforms.mo +0 -0
- wtforms-3.2.2/tests/fields/test_datetime.py +0 -72
- wtforms-3.2.2/tests/fields/test_datetimelocal.py +0 -74
- wtforms-3.2.2/tests/fields/test_select.py +0 -285
- wtforms-3.2.2/tests/validators/test_regexp.py +0 -64
- wtforms-3.2.2/tests/validators/test_url.py +0 -76
- {wtforms-3.2.2 → wtforms-3.3.0b2}/.gitignore +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/LICENSE.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/README.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/Makefile +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/_static/logo_joined.svg +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/_static/logo_split.svg +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/_static/logo_square.svg +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/changes.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/contributing.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/csrf.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/ext.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/faq.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/i18n.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/index.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/license.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/make.bat +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/meta.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/docs/whats_new.rst +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/hatch_build.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/csrf/__init__.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/csrf/core.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/csrf/session.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/i18n.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/de/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/el/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/es/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/et/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/he/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/it/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/ta/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/wtforms.pot +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/meta.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/src/wtforms/utils.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/__init__.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/common.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/__init__.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_boolean.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_color.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_custom.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_defaults.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_field.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_file.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_filters.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_flags.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_hidden.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_label.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_month.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_password.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_string.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_submit.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/fields/test_textarea.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/test_csrf.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/test_form.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/test_i18n.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/test_locale_babel.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/test_webob.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/__init__.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_anyof.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_data_required.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_disabled.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_equal_to.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_input_required.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_ipaddress.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_lazy_proxy.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_length.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_mac_address.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_noneof.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_optional.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_readonly.py +0 -0
- {wtforms-3.2.2 → wtforms-3.3.0b2}/tests/validators/test_validators.py +0 -0
|
@@ -1,5 +1,108 @@
|
|
|
1
1
|
.. currentmodule:: wtforms
|
|
2
2
|
|
|
3
|
+
Version 3.3.0b2
|
|
4
|
+
---------------
|
|
5
|
+
|
|
6
|
+
Released 2026-05-24
|
|
7
|
+
|
|
8
|
+
- :class:`~fields.SelectField` ``choices`` accepts a shorthand dict
|
|
9
|
+
syntax: ``{value: label}`` for flat options and
|
|
10
|
+
``{label: {value: label}}`` for optgroups. :issue:`886`
|
|
11
|
+
- Split choice types by role: :class:`~fields.SelectChoice` and
|
|
12
|
+
:class:`~fields.DataListChoice` to declare options via ``choices=``,
|
|
13
|
+
:class:`~fields.Choice` for what ``iter_choices`` / ``iter_groups``
|
|
14
|
+
yield. :issue:`922`
|
|
15
|
+
- ``choices`` callables on :class:`~fields.SelectField` and
|
|
16
|
+
:class:`~datalist.DataList` may accept ``(form, field)``. Resolved
|
|
17
|
+
once per processing cycle. :issue:`922`
|
|
18
|
+
- Add :meth:`fields.Field.post_process` and
|
|
19
|
+
:meth:`form.BaseForm.post_process` hooks, propagated through
|
|
20
|
+
:class:`~fields.FormField` and :class:`~fields.FieldList` for
|
|
21
|
+
cross-field finalization. :issue:`922`
|
|
22
|
+
- Add ``Field._form`` and ``BaseForm._parent_form`` so fields and nested
|
|
23
|
+
forms can reach their enclosing form. Fix :class:`~fields.FieldList`
|
|
24
|
+
entries and :class:`~fields.SelectField` option subfields which
|
|
25
|
+
previously got ``form=None``. :issue:`922`
|
|
26
|
+
- Restore 3.2 compatibility for :class:`~fields.SelectField` subclasses:
|
|
27
|
+
tuple yields from :meth:`~fields.SelectFieldBase.iter_choices` /
|
|
28
|
+
:meth:`~fields.SelectFieldBase.iter_groups`,
|
|
29
|
+
3-positional :meth:`widgets.Select.render_option` overrides,
|
|
30
|
+
:meth:`~fields.SelectFieldBase.has_groups` /
|
|
31
|
+
:meth:`~fields.SelectFieldBase.iter_groups`, and ``self.choices``
|
|
32
|
+
keeping the user-supplied shape. Each emits a ``DeprecationWarning``;
|
|
33
|
+
will be removed in WTForms 4.0. :issue:`922`
|
|
34
|
+
|
|
35
|
+
Version 3.3.0b1
|
|
36
|
+
---------------
|
|
37
|
+
|
|
38
|
+
Released 2026-05-10
|
|
39
|
+
|
|
40
|
+
- Add :class:`~validators.DateRange`. ``min`` and ``max`` may be callables
|
|
41
|
+
to compute dynamic bounds; :class:`~validators.NumberRange` accepts
|
|
42
|
+
callables on the same terms. :pr:`787`
|
|
43
|
+
- Make :class:`~fields.FloatField` render as ``<input type="number">`` with
|
|
44
|
+
``step="any"``. :issue:`677` :pr:`679`
|
|
45
|
+
- :meth:`~fields.FieldList._add_entry` now routes through :meth:`~meta.DefaultMeta.bind_field`,
|
|
46
|
+
consistent with how the form constructor binds top-level fields. Custom ``Meta.bind_field``
|
|
47
|
+
overrides were silently bypassed for all ``FieldList`` entries. :pr:`891`
|
|
48
|
+
- Add `test_environment` support to :class:`~validators.Email`. :issue:`869` :pr:`894`
|
|
49
|
+
- Reset :class:`~fields.FieldList` index state when reprocessing entries. :issue:`874` :pr:`895`
|
|
50
|
+
- Let :class:`~widgets.RangeInput` accept `min` and `max` at construction time. :issue:`693` :pr:`898`
|
|
51
|
+
- Do not render a ``for`` attribute on the main label of :class:`~fields.RadioField`.
|
|
52
|
+
:issue:`775` :pr:`900`
|
|
53
|
+
- Deprecate :class:`~fields.DateTimeField`, which will be removed in WTForms 3.4. :issue:`831` :pr:`897`
|
|
54
|
+
- Clarify that :class:`~fields.SelectField` ``choices`` is re-evaluated at
|
|
55
|
+
every form instantiation, including callables, in the documentation. :issue:`875` :pr:`909`
|
|
56
|
+
- Render ``False`` as ``value="False"`` in :class:`~fields.SelectField` options
|
|
57
|
+
instead of omitting the attribute. :issue:`878` :pr:`908`
|
|
58
|
+
- Accept :class:`uuid.UUID` instances in the :class:`~validators.UUID` validator,
|
|
59
|
+
and translate unexpected types into :class:`~validators.ValidationError`
|
|
60
|
+
instead of letting :class:`TypeError` escape. :issue:`549` :pr:`769`
|
|
61
|
+
- Escape double quotes in :class:`~markupsafe.Markup` attribute values
|
|
62
|
+
passed to :func:`~widgets.html_params`, and return a
|
|
63
|
+
:class:`~markupsafe.Markup`. :issue:`801` :pr:`906` :pr:`907`
|
|
64
|
+
- Clarify documentation about filters. :issue:`859` :pr:`896`
|
|
65
|
+
- Let render-time ``checked`` kwargs take precedence over the field's value
|
|
66
|
+
in :class:`~widgets.CheckboxInput` and :class:`~widgets.RadioInput`.
|
|
67
|
+
:issue:`706` :pr:`910`
|
|
68
|
+
- Add a ``tz`` parameter to :class:`~fields.DateTimeLocalField` to associate a
|
|
69
|
+
timezone with the input. :issue:`833` :pr:`905`
|
|
70
|
+
- Add :class:`~fields.FieldList` ``insert_entry`` and indexed ``pop_entry``.
|
|
71
|
+
:issue:`256` :pr:`901` :pr:`912`
|
|
72
|
+
- Add :class:`~fields.ButtonField` to render ``<button type="submit">``
|
|
73
|
+
controls with string values. :issue:`784` :pr:`892`
|
|
74
|
+
- :class:`~fields.SelectField` refactor. Choices tuples and dicts are
|
|
75
|
+
deprecated in favor of :class:`~fields.Choice`. :pr:`739` :pr:`889`
|
|
76
|
+
- ``<option>`` HTML attributes can be passed using
|
|
77
|
+
:class:`~fields.Choice`. :issue:`692` :pr:`739` :pr:`889`
|
|
78
|
+
- Add :meth:`fields.Choice.from_enum` to build choices from an
|
|
79
|
+
:class:`enum.Enum` class, and let :class:`~fields.SelectField` accept an
|
|
80
|
+
Enum class as ``coerce``. :issue:`338` :pr:`916`
|
|
81
|
+
- Defer to ``email_validator``'s module-level defaults for the
|
|
82
|
+
:class:`~validators.Email` ``test_environment``, ``allow_smtputf8`` and
|
|
83
|
+
``allow_empty_local`` options. :issue:`915` :pr:`917`
|
|
84
|
+
- Add ``invalid_value_message`` and ``invalid_choice_message`` keyword
|
|
85
|
+
arguments for customizing built-in field error messages. On
|
|
86
|
+
``SelectMultipleField``, ``invalid_choice_message`` may also be a callable
|
|
87
|
+
for custom pluralization. :issue:`39` :issue:`832` :pr:`902`
|
|
88
|
+
- :class:`~fields.SelectField` and :class:`~fields.SelectMultipleField`
|
|
89
|
+
``pre_validate`` now short-circuits when ``process_formdata`` already
|
|
90
|
+
produced an error, so a single coercion failure no longer surfaces as both
|
|
91
|
+
an "invalid value" and an "invalid choice" error. :pr:`902`
|
|
92
|
+
- Add a ``matcher`` keyword argument to :class:`~validators.Regexp` to
|
|
93
|
+
override the matching function. Defaults to :func:`re.match`; pass
|
|
94
|
+
:func:`re.search` or :func:`re.fullmatch` to change the anchoring
|
|
95
|
+
behaviour. :issue:`867` :pr:`918`
|
|
96
|
+
- Add an ``html_pattern`` keyword argument to :class:`~validators.Regexp`
|
|
97
|
+
to control the HTML ``pattern`` attribute emitted on supporting widgets.
|
|
98
|
+
:pr:`759`
|
|
99
|
+
- :class:`~validators.URL` validator is based on ``urllib.parse.urlparse``
|
|
100
|
+
instead of a regex. New keyword arguments: ``allow_userinfo`` for
|
|
101
|
+
accepting ``user:password@host`` URLs; ``schemes``
|
|
102
|
+
(default ``("http", "https")``) is the allowlist of accepted schemes
|
|
103
|
+
:pr:`847`
|
|
104
|
+
- Add native :class:`DataList` support. :issue:`653` :pr:`921`
|
|
105
|
+
|
|
3
106
|
Version 3.2.2
|
|
4
107
|
-------------
|
|
5
108
|
|
|
@@ -44,7 +147,6 @@ Released 2024-10-20
|
|
|
44
147
|
If you need to keep the old behavior you can set the ``_form_error_key``
|
|
45
148
|
parameter of your form to :data:`None`.
|
|
46
149
|
|
|
47
|
-
|
|
48
150
|
Version 3.1.2
|
|
49
151
|
-------------
|
|
50
152
|
|
|
@@ -13,6 +13,7 @@ release, version = get_version("WTForms")
|
|
|
13
13
|
master_doc = "index"
|
|
14
14
|
extensions = [
|
|
15
15
|
"sphinx.ext.autodoc",
|
|
16
|
+
"sphinx.ext.extlinks",
|
|
16
17
|
"sphinx.ext.intersphinx",
|
|
17
18
|
"sphinx.ext.viewcode",
|
|
18
19
|
"pallets_sphinx_themes",
|
|
@@ -22,6 +23,20 @@ extensions = [
|
|
|
22
23
|
intersphinx_mapping = {
|
|
23
24
|
"python": ("https://docs.python.org/3/", None),
|
|
24
25
|
}
|
|
26
|
+
extlinks = {
|
|
27
|
+
"mdn-tag": (
|
|
28
|
+
"https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/%s",
|
|
29
|
+
"<%s>",
|
|
30
|
+
),
|
|
31
|
+
"mdn-attr": (
|
|
32
|
+
"https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/%s",
|
|
33
|
+
"%s",
|
|
34
|
+
),
|
|
35
|
+
"mdn-input": (
|
|
36
|
+
"https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/%s",
|
|
37
|
+
'<input type="%s">',
|
|
38
|
+
),
|
|
39
|
+
}
|
|
25
40
|
issues_github_path = "pallets-eco/wtforms"
|
|
26
41
|
|
|
27
42
|
# HTML -----------------------------------------------------------------
|
|
@@ -133,6 +133,22 @@ providing with data. Typically, you will want to assign the values manually, but
|
|
|
133
133
|
for this simple case it's perfect. It can also be useful for CRUD and admin
|
|
134
134
|
forms.
|
|
135
135
|
|
|
136
|
+
The same pattern extends to a collection of objects: a
|
|
137
|
+
:class:`~wtforms.fields.FormField` describes one item, a
|
|
138
|
+
:class:`~wtforms.fields.FieldList` holds the list, and ``obj`` /
|
|
139
|
+
``populate_obj`` thread through both ends::
|
|
140
|
+
|
|
141
|
+
class ItemForm(Form):
|
|
142
|
+
name = StringField()
|
|
143
|
+
quantity = IntegerField()
|
|
144
|
+
|
|
145
|
+
class CartForm(Form):
|
|
146
|
+
items = FieldList(FormField(ItemForm))
|
|
147
|
+
|
|
148
|
+
form = CartForm(request.form, obj=cart) # cart.items == [item1, item2, ...]
|
|
149
|
+
if form.validate():
|
|
150
|
+
form.populate_obj(cart)
|
|
151
|
+
|
|
136
152
|
|
|
137
153
|
Exploring in the console
|
|
138
154
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
|
@@ -42,6 +42,9 @@ The Field base class
|
|
|
42
42
|
|
|
43
43
|
.. automethod:: __init__
|
|
44
44
|
|
|
45
|
+
Built-in field processing messages can be customized per field with
|
|
46
|
+
``invalid_value_message=``.
|
|
47
|
+
|
|
45
48
|
**Validation**
|
|
46
49
|
|
|
47
50
|
To validate the field, call its `validate` method, providing a form and any
|
|
@@ -135,7 +138,7 @@ The Field base class
|
|
|
135
138
|
.. attribute:: label
|
|
136
139
|
|
|
137
140
|
This is a :class:`Label` instance which when evaluated as a string
|
|
138
|
-
returns an HTML
|
|
141
|
+
returns an HTML :mdn-tag:`label` construct.
|
|
139
142
|
|
|
140
143
|
.. attribute:: default
|
|
141
144
|
|
|
@@ -220,6 +223,13 @@ refer to a single input from the form.
|
|
|
220
223
|
|
|
221
224
|
.. autoclass:: DateTimeField(default field arguments, format='%Y-%m-%d %H:%M:%S')
|
|
222
225
|
|
|
226
|
+
.. warning::
|
|
227
|
+
:class:`DateTimeField` renders ``<input type="datetime">``, which is
|
|
228
|
+
obsolete in HTML and not broadly supported by browsers. Use
|
|
229
|
+
:class:`DateTimeLocalField` instead.
|
|
230
|
+
|
|
231
|
+
:class:`DateTimeField` is deprecated and will be removed in WTForms 3.4.
|
|
232
|
+
|
|
223
233
|
.. autoclass:: DateTimeLocalField(default field arguments, format='%Y-%m-%d %H:%M:%S')
|
|
224
234
|
|
|
225
235
|
.. autoclass:: DecimalField(default field arguments, places=2, rounding=None, use_locale=False, number_format=None)
|
|
@@ -233,7 +243,7 @@ refer to a single input from the form.
|
|
|
233
243
|
Example usage::
|
|
234
244
|
|
|
235
245
|
class UploadForm(Form):
|
|
236
|
-
image = FileField('Image File', [validators.regexp('^[^/\\]\.jpg$')])
|
|
246
|
+
image = FileField('Image File', [validators.regexp(r'^[^/\\]\.jpg$', matcher=re.fullmatch)])
|
|
237
247
|
description = TextAreaField('Image Description')
|
|
238
248
|
|
|
239
249
|
def validate_image(form, field):
|
|
@@ -260,7 +270,28 @@ refer to a single input from the form.
|
|
|
260
270
|
|
|
261
271
|
.. autoclass:: MonthField(default field arguments, format='%Y-%m')
|
|
262
272
|
|
|
263
|
-
.. autoclass::
|
|
273
|
+
.. autoclass:: SearchField(default field arguments)
|
|
274
|
+
|
|
275
|
+
.. autoclass:: StringField(default field arguments)
|
|
276
|
+
|
|
277
|
+
.. code-block:: jinja
|
|
278
|
+
|
|
279
|
+
{{ form.username(size=30, maxlength=50) }}
|
|
280
|
+
|
|
281
|
+
.. autoclass:: TelField(default field arguments)
|
|
282
|
+
|
|
283
|
+
.. autoclass:: TimeField(default field arguments, format='%H:%M')
|
|
284
|
+
|
|
285
|
+
.. autoclass:: URLField(default field arguments)
|
|
286
|
+
|
|
287
|
+
Choice Fields
|
|
288
|
+
-------------
|
|
289
|
+
|
|
290
|
+
.. autoclass:: SelectChoice
|
|
291
|
+
|
|
292
|
+
.. autoclass:: Choice
|
|
293
|
+
|
|
294
|
+
.. autoclass:: RadioField(default field arguments, choices=None, coerce=str)
|
|
264
295
|
|
|
265
296
|
.. code-block:: jinja
|
|
266
297
|
|
|
@@ -272,49 +303,82 @@ refer to a single input from the form.
|
|
|
272
303
|
{% endfor %}
|
|
273
304
|
|
|
274
305
|
Simply outputting the field without iterating its subfields will result in
|
|
275
|
-
a
|
|
306
|
+
a :mdn-tag:`ul` list of radio choices.
|
|
307
|
+
|
|
276
308
|
|
|
277
|
-
.. class:: SelectField(default field arguments, choices=
|
|
309
|
+
.. class:: SelectField(default field arguments, choices=None, coerce=str, option_widget=None, validate_choice=True, invalid_value_message=None, invalid_choice_message=None)
|
|
278
310
|
|
|
279
311
|
Select fields take a ``choices`` parameter which is either:
|
|
280
312
|
|
|
281
|
-
* a list of
|
|
313
|
+
* a list of :class:`SelectChoice`.
|
|
282
314
|
It can also be a list of only values, in which case the value is used
|
|
283
|
-
as the label.
|
|
284
|
-
HTML
|
|
315
|
+
as the label. The :class:`SelectChoice` ``render_kw`` mapping is rendered as
|
|
316
|
+
HTML :mdn-tag:`option` parameters. The value can be of any
|
|
285
317
|
type, but because form data is sent to the browser as strings, you
|
|
286
318
|
will need to provide a ``coerce`` function that converts a string
|
|
287
319
|
back to the expected type.
|
|
288
|
-
* a
|
|
289
|
-
* a function taking no argument, and returning either a list or a dictionary.
|
|
320
|
+
* a function taking no argument, and returning a list of :class:`SelectChoice`.
|
|
290
321
|
|
|
291
322
|
|
|
292
323
|
**Select fields with static choice values**::
|
|
293
324
|
|
|
294
325
|
class PastebinEntry(Form):
|
|
295
|
-
language = SelectField('Programming Language', choices=[
|
|
326
|
+
language = SelectField('Programming Language', choices=[
|
|
327
|
+
SelectChoice('cpp', 'C++'),
|
|
328
|
+
SelectChoice('py', 'Python'),
|
|
329
|
+
SelectChoice('text', 'Plain Text'),
|
|
330
|
+
])
|
|
331
|
+
|
|
332
|
+
Note that the `choices` keyword is evaluated each time the form is
|
|
333
|
+
instantiated, so callables passed as `choices` are re-evaluated per instance.
|
|
334
|
+
For dynamic options, either pass a callable to `choices` or assign
|
|
335
|
+
`field.choices` after instantiation. Any submitted choices which are not in
|
|
336
|
+
the given choices list will cause validation on the field to fail. If this
|
|
337
|
+
option cannot be applied to your problem you may wish to skip choice
|
|
338
|
+
validation (see below).
|
|
339
|
+
|
|
340
|
+
**Select fields with ``<optgroup>``**::
|
|
341
|
+
|
|
342
|
+
Use :class:`SelectChoice` to assign an option to an ``<optgroup>``.
|
|
296
343
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
344
|
+
class PastebinEntry(Form):
|
|
345
|
+
language = SelectField('Programming Language', choices=[
|
|
346
|
+
SelectChoice('cpp', 'C++', optgroup='Compiled'),
|
|
347
|
+
SelectChoice('rs', 'Rust', optgroup='Compiled'),
|
|
348
|
+
SelectChoice('py', 'Python', optgroup='Interpreted'),
|
|
349
|
+
SelectChoice('text', 'Plain Text'),
|
|
350
|
+
])
|
|
302
351
|
|
|
303
352
|
**Select fields with dynamic choice values**::
|
|
304
353
|
|
|
354
|
+
def available_groups():
|
|
355
|
+
return [SelectChoice(g.id, g.name) for g in Group.query.order_by('name')]
|
|
356
|
+
|
|
305
357
|
class UserDetails(Form):
|
|
306
|
-
group_id = SelectField('Group', coerce=int)
|
|
358
|
+
group_id = SelectField('Group', coerce=int, choices=available_groups)
|
|
307
359
|
|
|
308
360
|
def edit_user(request, id):
|
|
309
361
|
user = User.query.get(id)
|
|
310
362
|
form = UserDetails(request.POST, obj=user)
|
|
311
|
-
form.group_id.choices = [(g.id, g.name) for g in Group.query.order_by('name')]
|
|
312
363
|
|
|
313
|
-
Note we
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
:
|
|
364
|
+
Note we passed a callable to `choices` rather than a static list; it is
|
|
365
|
+
invoked each time the form is instantiated, producing fresh options per
|
|
366
|
+
request. If the choices depend on data only available in the view, you can
|
|
367
|
+
instead assign `form.group_id.choices` after instantiation. The `coerce`
|
|
368
|
+
keyword arg to :class:`~wtforms.fields.SelectField` says that we use
|
|
369
|
+
:func:`int()` to coerce form data. The default coerce is :func:`str()`.
|
|
370
|
+
|
|
371
|
+
The callable may optionally accept ``(form, field)`` as positional
|
|
372
|
+
arguments. When this signature is used, the callable is invoked after the
|
|
373
|
+
whole form has been processed, so it can read ``field.data`` and the data
|
|
374
|
+
of any other field on the form::
|
|
375
|
+
|
|
376
|
+
def available_groups(form, field):
|
|
377
|
+
return [SelectChoice(g.id, g.name) for g in form.tenant.data.groups]
|
|
378
|
+
|
|
379
|
+
class UserDetails(Form):
|
|
380
|
+
tenant = QuerySelectField('Tenant', ...)
|
|
381
|
+
group_id = SelectField('Group', coerce=int, choices=available_groups)
|
|
318
382
|
|
|
319
383
|
**Coerce function example**::
|
|
320
384
|
|
|
@@ -324,11 +388,41 @@ refer to a single input from the form.
|
|
|
324
388
|
return value
|
|
325
389
|
|
|
326
390
|
class NonePossible(Form):
|
|
327
|
-
my_select_field = SelectField('Select an option', choices=[
|
|
391
|
+
my_select_field = SelectField('Select an option', choices=[
|
|
392
|
+
SelectChoice('1', 'Option 1'),
|
|
393
|
+
SelectChoice('2', 'Option 2'),
|
|
394
|
+
SelectChoice('None', 'No option'),
|
|
395
|
+
], coerce=coerce_none)
|
|
328
396
|
|
|
329
397
|
Note when the option None is selected a 'None' str will be passed. By using a coerce
|
|
330
398
|
function the 'None' str will be converted to None.
|
|
331
399
|
|
|
400
|
+
**Select fields backed by an Enum**::
|
|
401
|
+
|
|
402
|
+
from enum import Enum
|
|
403
|
+
|
|
404
|
+
class Color(Enum):
|
|
405
|
+
RED = 1
|
|
406
|
+
GREEN = 2
|
|
407
|
+
BLUE = 3
|
|
408
|
+
|
|
409
|
+
class PaintForm(Form):
|
|
410
|
+
color = SelectField(choices=SelectChoice.from_enum(Color), coerce=Color)
|
|
411
|
+
|
|
412
|
+
:meth:`SelectChoice.from_enum` builds the option list from the Enum items;
|
|
413
|
+
the HTML ``value`` of each option is the item's ``name``. Passing the
|
|
414
|
+
Enum class itself as ``coerce`` installs the matching coercion, so
|
|
415
|
+
``form.color.data`` is a ``Color`` item after submit. Pre-selecting
|
|
416
|
+
works the usual way, with an Enum item: ``PaintForm(color=Color.RED)``.
|
|
417
|
+
|
|
418
|
+
By default the option label is ``str(item)`` if the Enum defines its
|
|
419
|
+
own ``__str__`` (also the case for :class:`enum.StrEnum`), otherwise
|
|
420
|
+
``item.name``. To customise, pass a ``label`` callable taking an Enum
|
|
421
|
+
item and returning the label string::
|
|
422
|
+
|
|
423
|
+
SelectChoice.from_enum(Color, label=lambda item: item.name.title())
|
|
424
|
+
# → [SelectChoice('RED', 'Red'), SelectChoice('GREEN', 'Green'), SelectChoice('BLUE', 'Blue')]
|
|
425
|
+
|
|
332
426
|
**Skipping choice validation**::
|
|
333
427
|
|
|
334
428
|
class DynamicSelectForm(Form):
|
|
@@ -349,33 +443,31 @@ refer to a single input from the form.
|
|
|
349
443
|
a list of fields each representing an option. The rendering of this can be
|
|
350
444
|
further controlled by specifying `option_widget=`.
|
|
351
445
|
|
|
352
|
-
|
|
446
|
+
Use ``invalid_value_message=`` to customize errors raised when submitted
|
|
447
|
+
data cannot be coerced, and ``invalid_choice_message=`` to customize errors
|
|
448
|
+
raised when the value is not present in ``choices``.
|
|
353
449
|
|
|
354
|
-
.. autoclass:: SelectMultipleField(default field arguments, choices=
|
|
450
|
+
.. autoclass:: SelectMultipleField(default field arguments, choices=None, coerce=str, option_widget=None, invalid_value_message=None, invalid_choice_message=None)
|
|
355
451
|
|
|
356
452
|
The data on the SelectMultipleField is stored as a list of objects, each of
|
|
357
453
|
which is checked and coerced from the form input. Any submitted choices
|
|
358
454
|
which are not in the given choices list will cause validation on the field
|
|
359
455
|
to fail.
|
|
360
456
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
.. autoclass:: StringField(default field arguments)
|
|
364
|
-
|
|
365
|
-
.. code-block:: jinja
|
|
366
|
-
|
|
367
|
-
{{ form.username(size=30, maxlength=50) }}
|
|
368
|
-
|
|
369
|
-
.. autoclass:: TelField(default field arguments)
|
|
457
|
+
Submit fields
|
|
458
|
+
-------------
|
|
370
459
|
|
|
371
|
-
|
|
460
|
+
WTForms provides boolean and string-based submit controls.
|
|
372
461
|
|
|
373
|
-
.. autoclass::
|
|
462
|
+
.. autoclass:: ButtonField(default field arguments)
|
|
374
463
|
|
|
464
|
+
.. autoclass:: SubmitField(default field arguments)
|
|
375
465
|
|
|
376
466
|
Convenience Fields
|
|
377
467
|
------------------
|
|
378
468
|
|
|
469
|
+
.. currentmodule:: wtforms.fields
|
|
470
|
+
|
|
379
471
|
.. autoclass:: HiddenField(default field arguments)
|
|
380
472
|
|
|
381
473
|
HiddenField is useful for providing data from a model or the application to
|
|
@@ -436,11 +528,24 @@ complex data structures such as lists and nested objects can be represented.
|
|
|
436
528
|
.. autoclass:: FieldList(unbound_field, default field arguments, min_entries=0, max_entries=None, separator='-')
|
|
437
529
|
|
|
438
530
|
**Note**: Due to a limitation in how HTML sends values, FieldList cannot enclose
|
|
439
|
-
:class:`BooleanField
|
|
531
|
+
:class:`BooleanField`, :class:`ButtonField`, or :class:`SubmitField`
|
|
532
|
+
instances.
|
|
440
533
|
|
|
441
534
|
.. automethod:: append_entry([data])
|
|
535
|
+
.. automethod:: insert_entry(index[, data])
|
|
442
536
|
.. automethod:: pop_entry
|
|
443
537
|
|
|
538
|
+
``append_entry`` and ``insert_entry`` accept Python object data for the new
|
|
539
|
+
entry, not submitted formdata. For simple enclosed fields this is usually a
|
|
540
|
+
scalar value. For ``FieldList(FormField(...))``, this may be a dict or an
|
|
541
|
+
object that the enclosed form can process, similar to passing ``data`` or
|
|
542
|
+
``obj`` when constructing that form.
|
|
543
|
+
|
|
544
|
+
Entries are kept with consecutive indices: ``insert_entry``,
|
|
545
|
+
``pop_entry`` and rebuilds from formdata renumber entries so that each
|
|
546
|
+
entry's ``index``, ``name`` and ``id`` reflect its position in
|
|
547
|
+
:attr:`entries`.
|
|
548
|
+
|
|
444
549
|
.. attribute:: entries
|
|
445
550
|
|
|
446
551
|
Each entry in a FieldList is actually an instance of the field you
|
|
@@ -448,8 +553,8 @@ complex data structures such as lists and nested objects can be represented.
|
|
|
448
553
|
FieldList works as expected, and proxies to the enclosed entries list.
|
|
449
554
|
|
|
450
555
|
**Do not** resize the entries list directly, this will result in
|
|
451
|
-
undefined behavior. See `append_entry` and `pop_entry`
|
|
452
|
-
manipulate the list.
|
|
556
|
+
undefined behavior. See `append_entry`, `insert_entry`, and `pop_entry`
|
|
557
|
+
for ways you can manipulate the list.
|
|
453
558
|
|
|
454
559
|
.. automethod:: __iter__
|
|
455
560
|
.. automethod:: __len__
|
|
@@ -461,7 +566,7 @@ complex data structures such as lists and nested objects can be represented.
|
|
|
461
566
|
FormField::
|
|
462
567
|
|
|
463
568
|
class IMForm(Form):
|
|
464
|
-
protocol = SelectField(choices=[('aim', 'AIM'), ('msn', 'MSN')])
|
|
569
|
+
protocol = SelectField(choices=[SelectChoice('aim', 'AIM'), SelectChoice('msn', 'MSN')])
|
|
465
570
|
username = StringField()
|
|
466
571
|
|
|
467
572
|
class ContactForm(Form):
|
|
@@ -470,6 +575,92 @@ complex data structures such as lists and nested objects can be represented.
|
|
|
470
575
|
im_accounts = FieldList(FormField(IMForm))
|
|
471
576
|
|
|
472
577
|
|
|
578
|
+
Data Lists
|
|
579
|
+
----------
|
|
580
|
+
|
|
581
|
+
.. currentmodule:: wtforms
|
|
582
|
+
|
|
583
|
+
.. autoclass:: DataListChoice
|
|
584
|
+
|
|
585
|
+
.. class:: DataList(choices=None, *, render_kw=None, widget=None)
|
|
586
|
+
|
|
587
|
+
A :mdn-tag:`datalist` of suggestions. Unlike
|
|
588
|
+
:class:`~wtforms.fields.SelectField`, the
|
|
589
|
+
input stays a free-form text input: the browser displays the
|
|
590
|
+
suggestions but the user may type any value. Combine with
|
|
591
|
+
:class:`~wtforms.validators.AnyOf` if you need a closed set.
|
|
592
|
+
|
|
593
|
+
``choices`` is either a list of :class:`~wtforms.DataListChoice`
|
|
594
|
+
(or plain strings, in which case the string is used as both value
|
|
595
|
+
and label), or a callable invoked at render time. The callable
|
|
596
|
+
may take no argument (``fn()``) for a static list, or ``(field)``
|
|
597
|
+
to adapt the suggestions to the current field value at each
|
|
598
|
+
render — convenient for server-side filtering::
|
|
599
|
+
|
|
600
|
+
DataList(lambda field: search(field.data))
|
|
601
|
+
|
|
602
|
+
**Inline DataList owned by a field**
|
|
603
|
+
|
|
604
|
+
Pass a :class:`DataList` instance directly as ``datalist=`` to get
|
|
605
|
+
a field-owned datalist with a unique id derived from the field's
|
|
606
|
+
id::
|
|
607
|
+
|
|
608
|
+
class F(Form):
|
|
609
|
+
country = StringField(datalist=DataList(["FR", "US"]))
|
|
610
|
+
|
|
611
|
+
{{ form.country() }}
|
|
612
|
+
{{ form.country.datalist() }}
|
|
613
|
+
|
|
614
|
+
``{{ form.country() }}`` emits the ``<input list="...">`` and
|
|
615
|
+
``{{ form.country.datalist() }}`` emits the matching
|
|
616
|
+
:mdn-tag:`datalist` element. Both calls are explicit, like
|
|
617
|
+
:attr:`Field.errors` or :attr:`Field.label` — you decide where each
|
|
618
|
+
piece appears in the DOM.
|
|
619
|
+
|
|
620
|
+
Inside a :class:`~wtforms.fields.FieldList`, each entry binds its
|
|
621
|
+
own clone of the inline :class:`DataList`, so a callable
|
|
622
|
+
``choices`` sees each entry's own value::
|
|
623
|
+
|
|
624
|
+
class F(Form):
|
|
625
|
+
items = FieldList(
|
|
626
|
+
StringField(
|
|
627
|
+
datalist=DataList(lambda field: suggest(field.data))
|
|
628
|
+
),
|
|
629
|
+
min_entries=3,
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
{% for entry in form.items %}
|
|
633
|
+
{{ entry() }}
|
|
634
|
+
{{ entry.datalist() }}
|
|
635
|
+
{% endfor %}
|
|
636
|
+
|
|
637
|
+
**String references**
|
|
638
|
+
|
|
639
|
+
A field may also reference an existing datalist id directly by
|
|
640
|
+
string::
|
|
641
|
+
|
|
642
|
+
class F(Form):
|
|
643
|
+
field = StringField(datalist="external")
|
|
644
|
+
|
|
645
|
+
The ``list="external"`` attribute is emitted as-is — the user is
|
|
646
|
+
expected to render the :mdn-tag:`datalist` element themselves (in
|
|
647
|
+
a shared layout, a static template, or another form on the same
|
|
648
|
+
page). ``field.datalist()`` returns empty markup in this case, so
|
|
649
|
+
calling it unconditionally in templates is safe.
|
|
650
|
+
|
|
651
|
+
**Compatible fields**
|
|
652
|
+
|
|
653
|
+
Any field accepts a ``datalist=`` parameter, but only text-based
|
|
654
|
+
HTML inputs (``text``, ``search``, ``email``, ``tel``, ``url``,
|
|
655
|
+
``number``, ``range``, ``color``, date/time inputs) honor the
|
|
656
|
+
``list=`` attribute. Passing ``datalist=`` to a
|
|
657
|
+
:class:`~wtforms.fields.SelectField` or
|
|
658
|
+
:class:`~wtforms.fields.TextAreaField` is silently ignored by the
|
|
659
|
+
browser.
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
.. currentmodule:: wtforms.fields
|
|
663
|
+
|
|
473
664
|
Custom Fields
|
|
474
665
|
-------------
|
|
475
666
|
|
|
@@ -583,8 +774,7 @@ Additional Helper Classes
|
|
|
583
774
|
.. class:: Label
|
|
584
775
|
|
|
585
776
|
On all fields, the `label` property is an instance of this class.
|
|
586
|
-
Labels can be printed to yield a
|
|
587
|
-
``<label for="field_id">Label Text</label>``
|
|
777
|
+
Labels can be printed to yield a :mdn-tag:`label`
|
|
588
778
|
HTML tag enclosure. Similar to fields, you can also call the label with
|
|
589
779
|
additional html params.
|
|
590
780
|
|
|
@@ -151,11 +151,11 @@ The same principle applies for filters with the convention `filter_fieldname`::
|
|
|
151
151
|
class SignupForm(Form):
|
|
152
152
|
name = StringField('name')
|
|
153
153
|
|
|
154
|
-
def filter_name(form,
|
|
155
|
-
return
|
|
154
|
+
def filter_name(form, value):
|
|
155
|
+
return value.strip() if value is not None else value
|
|
156
156
|
|
|
157
157
|
Note that filters are applied after processing the default and incoming data,
|
|
158
|
-
but before validation.
|
|
158
|
+
but before validation. Filters should handle empty values such as `None`.
|
|
159
159
|
|
|
160
160
|
.. _using-forms:
|
|
161
161
|
|
|
@@ -124,10 +124,10 @@ Alternatively, you can create a field which does this by subclassing::
|
|
|
124
124
|
class HiddenInteger(IntegerField):
|
|
125
125
|
widget = HiddenInput()
|
|
126
126
|
|
|
127
|
-
Some fields support even more sophisticated customization.For example, what if
|
|
128
|
-
a multiple-select was desired where instead of using a multi-row
|
|
129
|
-
a series of
|
|
130
|
-
very easily::
|
|
127
|
+
Some fields support even more sophisticated customization. For example, what if
|
|
128
|
+
a multiple-select was desired where instead of using a multi-row
|
|
129
|
+
:mdn-tag:`select`, a series of :mdn-input:`checkbox` controls was used? By
|
|
130
|
+
using widgets, one can get that behavior very easily::
|
|
131
131
|
|
|
132
132
|
class MultiCheckboxField(SelectMultipleField):
|
|
133
133
|
"""
|
|
@@ -140,4 +140,4 @@ very easily::
|
|
|
140
140
|
option_widget = widgets.CheckboxInput()
|
|
141
141
|
|
|
142
142
|
By overriding `option_widget`, our new multiple-select when iterated will now
|
|
143
|
-
produce fields that render as
|
|
143
|
+
produce fields that render as :mdn-input:`checkbox` controls.
|
|
@@ -15,15 +15,15 @@ Built-in validators
|
|
|
15
15
|
|
|
16
16
|
.. autoclass:: wtforms.validators.DataRequired
|
|
17
17
|
|
|
18
|
-
This also sets the
|
|
19
|
-
fields it is used on. This flag causes the
|
|
18
|
+
This also sets the :mdn-attr:`required` :attr:`flag <wtforms.fields.Field.flags>` on
|
|
19
|
+
fields it is used on. This flag causes the :mdn-attr:`required` attribute to be
|
|
20
20
|
rendered in the tag, which prevents a request/response cycle for validation.
|
|
21
21
|
This behavior can be overridden in the following ways:
|
|
22
22
|
|
|
23
23
|
- Specifying ``required=False`` when rendering in the template.
|
|
24
24
|
- Making a custom a widget that doesn't set it.
|
|
25
|
-
- Rendering the
|
|
26
|
-
|
|
25
|
+
- Rendering the :mdn-attr:`novalidate` attribute on the :mdn-tag:`form`
|
|
26
|
+
tag, or the :mdn-attr:`formnovalidate` attribute on a submit button.
|
|
27
27
|
|
|
28
28
|
The ``required`` flag behavior also applies to the :class:`InputRequired` class.
|
|
29
29
|
|
|
@@ -57,6 +57,9 @@ Built-in validators
|
|
|
57
57
|
|
|
58
58
|
.. autoclass:: wtforms.validators.NumberRange
|
|
59
59
|
|
|
60
|
+
.. autoclass:: wtforms.validators.DateRange
|
|
61
|
+
|
|
62
|
+
|
|
60
63
|
.. autoclass:: wtforms.validators.Optional
|
|
61
64
|
|
|
62
65
|
This also sets the ``optional`` :attr:`flag <wtforms.fields.Field.flags>` on
|