WTForms 3.2.1__tar.gz → 3.3.0b1__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.1 → wtforms-3.3.0b1}/.gitignore +1 -0
- wtforms-3.3.0b1/.hgignore +1 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/CHANGES.rst +82 -1
- {wtforms-3.2.1 → wtforms-3.3.0b1}/PKG-INFO +5 -12
- {wtforms-3.2.1 → wtforms-3.3.0b1}/README.rst +0 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/conf.py +15 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/crash_course.rst +16 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/faq.rst +1 -1
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/fields.rst +219 -43
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/forms.rst +3 -3
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/specific_problems.rst +5 -5
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/validators.rst +7 -4
- wtforms-3.3.0b1/docs/widgets.rst +162 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/pyproject.toml +67 -4
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/__init__.py +9 -1
- wtforms-3.3.0b1/src/wtforms/datalist.py +52 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/fields/__init__.py +6 -0
- wtforms-3.3.0b1/src/wtforms/fields/choices.py +338 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/fields/core.py +26 -4
- wtforms-3.3.0b1/src/wtforms/fields/datetime.py +293 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/fields/form.py +1 -2
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/fields/list.py +85 -18
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/fields/numeric.py +35 -13
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/fields/simple.py +68 -16
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/form.py +5 -1
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/README.md +12 -1
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ar/LC_MESSAGES/wtforms.po +8 -8
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/bg/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ca/LC_MESSAGES/wtforms.po +8 -9
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/cs_CZ/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/cy/LC_MESSAGES/wtforms.po +9 -8
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/de/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/de/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/de_CH/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/el/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/el/LC_MESSAGES/wtforms.po +7 -7
- wtforms-3.3.0b1/src/wtforms/locale/en/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/en/LC_MESSAGES/wtforms.po +59 -43
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/es/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/es/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/et/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/et/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/fa/LC_MESSAGES/wtforms.po +6 -6
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/fi/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/fr/LC_MESSAGES/wtforms.po +8 -9
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/he/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/he/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/hu/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/it/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/it/LC_MESSAGES/wtforms.po +10 -10
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ja/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/kk/LC_MESSAGES/wtforms.po +7 -8
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ko/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/nb/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/nl/LC_MESSAGES/wtforms.po +9 -11
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/pl/LC_MESSAGES/wtforms.po +9 -10
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/pt/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ro/LC_MESSAGES/wtforms.po +11 -11
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/ru/LC_MESSAGES/wtforms.po +8 -9
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/sk/LC_MESSAGES/wtforms.po +5 -6
- wtforms-3.3.0b1/src/wtforms/locale/sr/LC_MESSAGES/wtforms.mo +0 -0
- wtforms-3.3.0b1/src/wtforms/locale/sr/LC_MESSAGES/wtforms.po +189 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/sv/LC_MESSAGES/wtforms.po +5 -6
- wtforms-3.3.0b1/src/wtforms/locale/ta/LC_MESSAGES/wtforms.mo +0 -0
- wtforms-3.3.0b1/src/wtforms/locale/ta/LC_MESSAGES/wtforms.po +186 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/tr/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/uk/LC_MESSAGES/wtforms.po +6 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/zh/LC_MESSAGES/wtforms.po +7 -7
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.mo +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/zh_TW/LC_MESSAGES/wtforms.po +5 -6
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/validators.py +227 -34
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/widgets/__init__.py +4 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/widgets/core.py +147 -80
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/common.py +1 -1
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/conftest.py +7 -1
- wtforms-3.3.0b1/tests/fields/test_button.py +88 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_date.py +9 -0
- wtforms-3.3.0b1/tests/fields/test_datetime.py +116 -0
- wtforms-3.3.0b1/tests/fields/test_datetimelocal.py +141 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_decimal.py +7 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_float.py +13 -2
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_html5.py +62 -9
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_integer.py +9 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_label.py +2 -4
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_list.py +163 -1
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_radio.py +14 -6
- wtforms-3.3.0b1/tests/fields/test_select.py +478 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_selectmultiple.py +95 -14
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_time.py +9 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_week.py +9 -0
- wtforms-3.3.0b1/tests/test_datalist.py +371 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/test_locale_babel.py +3 -3
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/test_widgets.py +89 -5
- wtforms-3.3.0b1/tests/validators/test_date_range.py +126 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_disabled.py +3 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_email.py +47 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_number_range.py +19 -0
- wtforms-3.3.0b1/tests/validators/test_regexp.py +106 -0
- wtforms-3.3.0b1/tests/validators/test_url.py +143 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_uuid.py +16 -1
- wtforms-3.2.1/docs/requirements.txt +0 -5
- wtforms-3.2.1/docs/widgets.rst +0 -104
- wtforms-3.2.1/src/wtforms/fields/choices.py +0 -229
- wtforms-3.2.1/src/wtforms/fields/datetime.py +0 -170
- wtforms-3.2.1/src/wtforms/locale/en/LC_MESSAGES/wtforms.mo +0 -0
- wtforms-3.2.1/tests/fields/test_datetime.py +0 -72
- wtforms-3.2.1/tests/fields/test_datetimelocal.py +0 -74
- wtforms-3.2.1/tests/fields/test_select.py +0 -285
- wtforms-3.2.1/tests/validators/test_regexp.py +0 -64
- wtforms-3.2.1/tests/validators/test_url.py +0 -76
- wtforms-3.2.1/tox.ini +0 -37
- {wtforms-3.2.1 → wtforms-3.3.0b1}/LICENSE.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/Makefile +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/_static/logo_joined.svg +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/_static/logo_split.svg +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/_static/logo_square.svg +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/changes.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/contributing.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/csrf.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/ext.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/i18n.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/index.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/license.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/make.bat +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/meta.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/docs/whats_new.rst +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/hatch_build.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/csrf/__init__.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/csrf/core.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/csrf/session.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/i18n.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/locale/wtforms.pot +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/meta.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/src/wtforms/utils.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/__init__.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/__init__.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_boolean.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_color.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_custom.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_defaults.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_field.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_file.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_filters.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_flags.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_form.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_hidden.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_month.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_password.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_string.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_submit.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/fields/test_textarea.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/test_csrf.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/test_form.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/test_i18n.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/test_webob.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/__init__.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_anyof.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_data_required.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_equal_to.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_input_required.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_ipaddress.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_lazy_proxy.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_length.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_mac_address.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_noneof.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_optional.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_readonly.py +0 -0
- {wtforms-3.2.1 → wtforms-3.3.0b1}/tests/validators/test_validators.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.dotfiles/mercurial/.hgignore
|
|
@@ -1,5 +1,87 @@
|
|
|
1
1
|
.. currentmodule:: wtforms
|
|
2
2
|
|
|
3
|
+
Version 3.3.0b1
|
|
4
|
+
---------------
|
|
5
|
+
|
|
6
|
+
Released 2026-05-10
|
|
7
|
+
|
|
8
|
+
- Add :class:`~validators.DateRange`. ``min`` and ``max`` may be callables
|
|
9
|
+
to compute dynamic bounds; :class:`~validators.NumberRange` accepts
|
|
10
|
+
callables on the same terms. :pr:`787`
|
|
11
|
+
- Make :class:`~fields.FloatField` render as ``<input type="number">`` with
|
|
12
|
+
``step="any"``. :issue:`677` :pr:`679`
|
|
13
|
+
- :meth:`~fields.FieldList._add_entry` now routes through :meth:`~meta.DefaultMeta.bind_field`,
|
|
14
|
+
consistent with how the form constructor binds top-level fields. Custom ``Meta.bind_field``
|
|
15
|
+
overrides were silently bypassed for all ``FieldList`` entries.
|
|
16
|
+
- Add `test_environment` support to :class:`~validators.Email`. :issue:`869`
|
|
17
|
+
- Reset :class:`~fields.FieldList` index state when reprocessing entries. :issue:`874`
|
|
18
|
+
- Let :class:`~widgets.RangeInput` accept `min` and `max` at construction time. :issue:`693`
|
|
19
|
+
- Do not render a ``for`` attribute on the main label of :class:`~fields.RadioField`.
|
|
20
|
+
:issue:`775`
|
|
21
|
+
- Deprecate :class:`~fields.DateTimeField`, which will be removed in WTForms 3.4. :issue:`831`
|
|
22
|
+
- Clarify that :class:`~fields.SelectField` ``choices`` is re-evaluated at
|
|
23
|
+
every form instantiation, including callables, in the documentation. :issue:`875`
|
|
24
|
+
- Render ``False`` as ``value="False"`` in :class:`~fields.SelectField` options
|
|
25
|
+
instead of omitting the attribute. :issue:`878`
|
|
26
|
+
- Accept :class:`uuid.UUID` instances in the :class:`~validators.UUID` validator,
|
|
27
|
+
and translate unexpected types into :class:`~validators.ValidationError`
|
|
28
|
+
instead of letting :class:`TypeError` escape. :issue:`549` :pr:`769`
|
|
29
|
+
- Escape double quotes in :class:`~markupsafe.Markup` attribute values
|
|
30
|
+
passed to :func:`~widgets.html_params`, and return a
|
|
31
|
+
:class:`~markupsafe.Markup`. :issue:`801`
|
|
32
|
+
- Clarify documentation about filters. :issue:`859`
|
|
33
|
+
- Let render-time ``checked`` kwargs take precedence over the field's value
|
|
34
|
+
in :class:`~widgets.CheckboxInput` and :class:`~widgets.RadioInput`.
|
|
35
|
+
:issue:`706`
|
|
36
|
+
- Add a ``tz`` parameter to :class:`~fields.DateTimeLocalField` to associate a
|
|
37
|
+
timezone with the input. :issue:`833`
|
|
38
|
+
- Add :class:`~fields.FieldList` ``insert_entry`` and indexed ``pop_entry``.
|
|
39
|
+
:issue:`256`
|
|
40
|
+
- :class:`~fields.FieldList` builds entries in the order keys appear in
|
|
41
|
+
``formdata``, instead of sorting them by numeric index. :issue:`256`
|
|
42
|
+
- Add :class:`~fields.ButtonField` to render ``<button type="submit">``
|
|
43
|
+
controls with string values. :issue:`784`
|
|
44
|
+
- :class:`~fields.SelectField` refactor. Choices tuples and dicts are
|
|
45
|
+
deprecated in favor of :class:`~fields.Choice`. :pr:`739`
|
|
46
|
+
- ``<option>`` HTML attributes can be passed using
|
|
47
|
+
:class:`~fields.Choice`. :issue:`692` :pr:`739`
|
|
48
|
+
- Add :meth:`fields.Choice.from_enum` to build choices from an
|
|
49
|
+
:class:`enum.Enum` class, and let :class:`~fields.SelectField` accept an
|
|
50
|
+
Enum class as ``coerce``. :issue:`338`
|
|
51
|
+
- Defer to ``email_validator``'s module-level defaults for the
|
|
52
|
+
:class:`~validators.Email` ``test_environment``, ``allow_smtputf8`` and
|
|
53
|
+
``allow_empty_local`` options. :issue:`915`
|
|
54
|
+
- Add ``invalid_value_message`` and ``invalid_choice_message`` keyword
|
|
55
|
+
arguments for customizing built-in field error messages. On
|
|
56
|
+
``SelectMultipleField``, ``invalid_choice_message`` may also be a callable
|
|
57
|
+
for custom pluralization. :issue:`39` :issue:`832`
|
|
58
|
+
- :class:`~fields.SelectField` and :class:`~fields.SelectMultipleField`
|
|
59
|
+
``pre_validate`` now short-circuits when ``process_formdata`` already
|
|
60
|
+
produced an error, so a single coercion failure no longer surfaces as both
|
|
61
|
+
an "invalid value" and an "invalid choice" error.
|
|
62
|
+
- Add a ``matcher`` keyword argument to :class:`~validators.Regexp` to
|
|
63
|
+
override the matching function. Defaults to :func:`re.match`; pass
|
|
64
|
+
:func:`re.search` or :func:`re.fullmatch` to change the anchoring
|
|
65
|
+
behaviour. :issue:`867`
|
|
66
|
+
- Add an ``html_pattern`` keyword argument to :class:`~validators.Regexp`
|
|
67
|
+
to control the HTML ``pattern`` attribute emitted on supporting widgets.
|
|
68
|
+
:issue:`759`
|
|
69
|
+
- :class:`~validators.URL` validator is based on ``urllib.parse.urlparse``
|
|
70
|
+
instead of a regex. New keyword arguments: ``allow_userinfo`` for
|
|
71
|
+
accepting ``user:password@host`` URLs; ``schemes``
|
|
72
|
+
(default ``("http", "https")``) is the allowlist of accepted schemes
|
|
73
|
+
:pr:`847`
|
|
74
|
+
- Add native :class:`DataList` support. :issue:`653`
|
|
75
|
+
|
|
76
|
+
Version 3.2.2
|
|
77
|
+
-------------
|
|
78
|
+
|
|
79
|
+
Released 2026-05-03
|
|
80
|
+
|
|
81
|
+
- Fix :class:`~validators.Disabled` validation with provided formdata. :pr:`880`
|
|
82
|
+
- End support for Python 3.9, start support for Python 3.14. :pr:`883`
|
|
83
|
+
- Add Tamil and Serbian translations.
|
|
84
|
+
|
|
3
85
|
Version 3.2.1
|
|
4
86
|
-------------
|
|
5
87
|
|
|
@@ -35,7 +117,6 @@ Released 2024-10-20
|
|
|
35
117
|
If you need to keep the old behavior you can set the ``_form_error_key``
|
|
36
118
|
parameter of your form to :data:`None`.
|
|
37
119
|
|
|
38
|
-
|
|
39
120
|
Version 3.1.2
|
|
40
121
|
-------------
|
|
41
122
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: WTForms
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0b1
|
|
4
4
|
Summary: Form validation and rendering for Python web development.
|
|
5
5
|
Project-URL: Documentation, https://wtforms.readthedocs.io
|
|
6
6
|
Project-URL: Changes, https://wtforms.readthedocs.io/changes
|
|
@@ -44,10 +44,10 @@ Classifier: License :: OSI Approved :: BSD License
|
|
|
44
44
|
Classifier: Operating System :: OS Independent
|
|
45
45
|
Classifier: Programming Language :: Python
|
|
46
46
|
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
47
|
-
Requires-Python: >=3.
|
|
48
|
-
Requires-Dist: markupsafe
|
|
47
|
+
Requires-Python: >=3.10
|
|
48
|
+
Requires-Dist: markupsafe>=1.1.1
|
|
49
49
|
Provides-Extra: email
|
|
50
|
-
Requires-Dist: email-validator; extra == 'email'
|
|
50
|
+
Requires-Dist: email-validator>=2.0; extra == 'email'
|
|
51
51
|
Description-Content-Type: text/x-rst
|
|
52
52
|
|
|
53
53
|
WTForms
|
|
@@ -86,11 +86,6 @@ with frameworks even better.
|
|
|
86
86
|
validators.
|
|
87
87
|
- `WTForms-SQLAlchemy`_ provides ORM-backed fields and form generation
|
|
88
88
|
from SQLAlchemy models.
|
|
89
|
-
- `WTForms-AppEngine`_ provides ORM-backed fields and form generation
|
|
90
|
-
from AppEnding db/ndb schema
|
|
91
|
-
- `WTForms-Django`_ provides ORM-backed fields and form generation
|
|
92
|
-
from Django models, as well as integration with Django's I18N
|
|
93
|
-
support.
|
|
94
89
|
- `WTForms-Bootstrap5`_ provides Bootstrap 5 favor renderer with
|
|
95
90
|
great customizability.
|
|
96
91
|
- `Starlette-WTF`_ integrates with Starlette and the FastAPI
|
|
@@ -101,8 +96,6 @@ with frameworks even better.
|
|
|
101
96
|
.. _Flask-WTF: https://flask-wtf.readthedocs.io/
|
|
102
97
|
.. _WTForms-Alchemy: https://wtforms-alchemy.readthedocs.io/
|
|
103
98
|
.. _WTForms-SQLAlchemy: https://github.com/pallets-eco/wtforms-sqlalchemy
|
|
104
|
-
.. _WTForms-AppEngine: https://github.com/pallets-eco/wtforms-appengine
|
|
105
|
-
.. _WTForms-Django: https://github.com/pallets-eco/wtforms-django
|
|
106
99
|
.. _WTForms-Bootstrap5: https://github.com/LaunchPlatform/wtforms-bootstrap5
|
|
107
100
|
.. _Starlette-WTF: https://github.com/muicss/starlette-wtf
|
|
108
101
|
.. _Bootstrap-Flask: https://github.com/helloflask/bootstrap-flask
|
|
@@ -34,11 +34,6 @@ with frameworks even better.
|
|
|
34
34
|
validators.
|
|
35
35
|
- `WTForms-SQLAlchemy`_ provides ORM-backed fields and form generation
|
|
36
36
|
from SQLAlchemy models.
|
|
37
|
-
- `WTForms-AppEngine`_ provides ORM-backed fields and form generation
|
|
38
|
-
from AppEnding db/ndb schema
|
|
39
|
-
- `WTForms-Django`_ provides ORM-backed fields and form generation
|
|
40
|
-
from Django models, as well as integration with Django's I18N
|
|
41
|
-
support.
|
|
42
37
|
- `WTForms-Bootstrap5`_ provides Bootstrap 5 favor renderer with
|
|
43
38
|
great customizability.
|
|
44
39
|
- `Starlette-WTF`_ integrates with Starlette and the FastAPI
|
|
@@ -49,8 +44,6 @@ with frameworks even better.
|
|
|
49
44
|
.. _Flask-WTF: https://flask-wtf.readthedocs.io/
|
|
50
45
|
.. _WTForms-Alchemy: https://wtforms-alchemy.readthedocs.io/
|
|
51
46
|
.. _WTForms-SQLAlchemy: https://github.com/pallets-eco/wtforms-sqlalchemy
|
|
52
|
-
.. _WTForms-AppEngine: https://github.com/pallets-eco/wtforms-appengine
|
|
53
|
-
.. _WTForms-Django: https://github.com/pallets-eco/wtforms-django
|
|
54
47
|
.. _WTForms-Bootstrap5: https://github.com/LaunchPlatform/wtforms-bootstrap5
|
|
55
48
|
.. _Starlette-WTF: https://github.com/muicss/starlette-wtf
|
|
56
49
|
.. _Bootstrap-Flask: https://github.com/helloflask/bootstrap-flask
|
|
@@ -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:: Choice
|
|
291
|
+
|
|
292
|
+
.. autoclass:: SelectChoice
|
|
293
|
+
|
|
294
|
+
.. autoclass:: RadioField(default field arguments, choices=None, coerce=str)
|
|
264
295
|
|
|
265
296
|
.. code-block:: jinja
|
|
266
297
|
|
|
@@ -272,49 +303,70 @@ 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:`Choice`.
|
|
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:`Choice` ``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:`Choice`.
|
|
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
|
+
Choice('cpp', 'C++'),
|
|
328
|
+
Choice('py', 'Python'),
|
|
329
|
+
Choice('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>``**::
|
|
296
341
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
342
|
+
Use :class:`SelectChoice` to assign an option to an ``<optgroup>``.
|
|
343
|
+
|
|
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 [Choice(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()`.
|
|
318
370
|
|
|
319
371
|
**Coerce function example**::
|
|
320
372
|
|
|
@@ -324,11 +376,41 @@ refer to a single input from the form.
|
|
|
324
376
|
return value
|
|
325
377
|
|
|
326
378
|
class NonePossible(Form):
|
|
327
|
-
my_select_field = SelectField('Select an option', choices=[
|
|
379
|
+
my_select_field = SelectField('Select an option', choices=[
|
|
380
|
+
Choice('1', 'Option 1'),
|
|
381
|
+
Choice('2', 'Option 2'),
|
|
382
|
+
Choice('None', 'No option'),
|
|
383
|
+
], coerce=coerce_none)
|
|
328
384
|
|
|
329
385
|
Note when the option None is selected a 'None' str will be passed. By using a coerce
|
|
330
386
|
function the 'None' str will be converted to None.
|
|
331
387
|
|
|
388
|
+
**Select fields backed by an Enum**::
|
|
389
|
+
|
|
390
|
+
from enum import Enum
|
|
391
|
+
|
|
392
|
+
class Color(Enum):
|
|
393
|
+
RED = 1
|
|
394
|
+
GREEN = 2
|
|
395
|
+
BLUE = 3
|
|
396
|
+
|
|
397
|
+
class PaintForm(Form):
|
|
398
|
+
color = SelectField(choices=Choice.from_enum(Color), coerce=Color)
|
|
399
|
+
|
|
400
|
+
:meth:`Choice.from_enum` builds the option list from the Enum items;
|
|
401
|
+
the HTML ``value`` of each option is the item's ``name``. Passing the
|
|
402
|
+
Enum class itself as ``coerce`` installs the matching coercion, so
|
|
403
|
+
``form.color.data`` is a ``Color`` item after submit. Pre-selecting
|
|
404
|
+
works the usual way, with an Enum item: ``PaintForm(color=Color.RED)``.
|
|
405
|
+
|
|
406
|
+
By default the option label is ``str(item)`` if the Enum defines its
|
|
407
|
+
own ``__str__`` (also the case for :class:`enum.StrEnum`), otherwise
|
|
408
|
+
``item.name``. To customise, pass a ``label`` callable taking an Enum
|
|
409
|
+
item and returning the label string::
|
|
410
|
+
|
|
411
|
+
Choice.from_enum(Color, label=lambda item: item.name.title())
|
|
412
|
+
# → [Choice('RED', 'Red'), Choice('GREEN', 'Green'), Choice('BLUE', 'Blue')]
|
|
413
|
+
|
|
332
414
|
**Skipping choice validation**::
|
|
333
415
|
|
|
334
416
|
class DynamicSelectForm(Form):
|
|
@@ -349,33 +431,31 @@ refer to a single input from the form.
|
|
|
349
431
|
a list of fields each representing an option. The rendering of this can be
|
|
350
432
|
further controlled by specifying `option_widget=`.
|
|
351
433
|
|
|
352
|
-
|
|
434
|
+
Use ``invalid_value_message=`` to customize errors raised when submitted
|
|
435
|
+
data cannot be coerced, and ``invalid_choice_message=`` to customize errors
|
|
436
|
+
raised when the value is not present in ``choices``.
|
|
353
437
|
|
|
354
|
-
.. autoclass:: SelectMultipleField(default field arguments, choices=
|
|
438
|
+
.. autoclass:: SelectMultipleField(default field arguments, choices=None, coerce=str, option_widget=None, invalid_value_message=None, invalid_choice_message=None)
|
|
355
439
|
|
|
356
440
|
The data on the SelectMultipleField is stored as a list of objects, each of
|
|
357
441
|
which is checked and coerced from the form input. Any submitted choices
|
|
358
442
|
which are not in the given choices list will cause validation on the field
|
|
359
443
|
to fail.
|
|
360
444
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
.. autoclass:: StringField(default field arguments)
|
|
364
|
-
|
|
365
|
-
.. code-block:: jinja
|
|
366
|
-
|
|
367
|
-
{{ form.username(size=30, maxlength=50) }}
|
|
445
|
+
Submit fields
|
|
446
|
+
-------------
|
|
368
447
|
|
|
369
|
-
|
|
448
|
+
WTForms provides boolean and string-based submit controls.
|
|
370
449
|
|
|
371
|
-
.. autoclass::
|
|
372
|
-
|
|
373
|
-
.. autoclass:: URLField(default field arguments)
|
|
450
|
+
.. autoclass:: ButtonField(default field arguments)
|
|
374
451
|
|
|
452
|
+
.. autoclass:: SubmitField(default field arguments)
|
|
375
453
|
|
|
376
454
|
Convenience Fields
|
|
377
455
|
------------------
|
|
378
456
|
|
|
457
|
+
.. currentmodule:: wtforms.fields
|
|
458
|
+
|
|
379
459
|
.. autoclass:: HiddenField(default field arguments)
|
|
380
460
|
|
|
381
461
|
HiddenField is useful for providing data from a model or the application to
|
|
@@ -436,11 +516,24 @@ complex data structures such as lists and nested objects can be represented.
|
|
|
436
516
|
.. autoclass:: FieldList(unbound_field, default field arguments, min_entries=0, max_entries=None, separator='-')
|
|
437
517
|
|
|
438
518
|
**Note**: Due to a limitation in how HTML sends values, FieldList cannot enclose
|
|
439
|
-
:class:`BooleanField
|
|
519
|
+
:class:`BooleanField`, :class:`ButtonField`, or :class:`SubmitField`
|
|
520
|
+
instances.
|
|
440
521
|
|
|
441
522
|
.. automethod:: append_entry([data])
|
|
523
|
+
.. automethod:: insert_entry(index[, data])
|
|
442
524
|
.. automethod:: pop_entry
|
|
443
525
|
|
|
526
|
+
``append_entry`` and ``insert_entry`` accept Python object data for the new
|
|
527
|
+
entry, not submitted formdata. For simple enclosed fields this is usually a
|
|
528
|
+
scalar value. For ``FieldList(FormField(...))``, this may be a dict or an
|
|
529
|
+
object that the enclosed form can process, similar to passing ``data`` or
|
|
530
|
+
``obj`` when constructing that form.
|
|
531
|
+
|
|
532
|
+
Entries are kept with consecutive indices: ``insert_entry``,
|
|
533
|
+
``pop_entry`` and rebuilds from formdata renumber entries so that each
|
|
534
|
+
entry's ``index``, ``name`` and ``id`` reflect its position in
|
|
535
|
+
:attr:`entries`.
|
|
536
|
+
|
|
444
537
|
.. attribute:: entries
|
|
445
538
|
|
|
446
539
|
Each entry in a FieldList is actually an instance of the field you
|
|
@@ -448,8 +541,8 @@ complex data structures such as lists and nested objects can be represented.
|
|
|
448
541
|
FieldList works as expected, and proxies to the enclosed entries list.
|
|
449
542
|
|
|
450
543
|
**Do not** resize the entries list directly, this will result in
|
|
451
|
-
undefined behavior. See `append_entry` and `pop_entry`
|
|
452
|
-
manipulate the list.
|
|
544
|
+
undefined behavior. See `append_entry`, `insert_entry`, and `pop_entry`
|
|
545
|
+
for ways you can manipulate the list.
|
|
453
546
|
|
|
454
547
|
.. automethod:: __iter__
|
|
455
548
|
.. automethod:: __len__
|
|
@@ -461,7 +554,7 @@ complex data structures such as lists and nested objects can be represented.
|
|
|
461
554
|
FormField::
|
|
462
555
|
|
|
463
556
|
class IMForm(Form):
|
|
464
|
-
protocol = SelectField(choices=[('aim', 'AIM'), ('msn', 'MSN')])
|
|
557
|
+
protocol = SelectField(choices=[Choice('aim', 'AIM'), Choice('msn', 'MSN')])
|
|
465
558
|
username = StringField()
|
|
466
559
|
|
|
467
560
|
class ContactForm(Form):
|
|
@@ -470,6 +563,90 @@ complex data structures such as lists and nested objects can be represented.
|
|
|
470
563
|
im_accounts = FieldList(FormField(IMForm))
|
|
471
564
|
|
|
472
565
|
|
|
566
|
+
Data Lists
|
|
567
|
+
----------
|
|
568
|
+
|
|
569
|
+
.. currentmodule:: wtforms
|
|
570
|
+
|
|
571
|
+
.. class:: DataList(choices=None, *, render_kw=None, widget=None)
|
|
572
|
+
|
|
573
|
+
A :mdn-tag:`datalist` of suggestions. Unlike
|
|
574
|
+
:class:`~wtforms.fields.SelectField`, the
|
|
575
|
+
input stays a free-form text input: the browser displays the
|
|
576
|
+
suggestions but the user may type any value. Combine with
|
|
577
|
+
:class:`~wtforms.validators.AnyOf` if you need a closed set.
|
|
578
|
+
|
|
579
|
+
``choices`` is either a list of :class:`~wtforms.fields.Choice`
|
|
580
|
+
(or plain strings, in which case the string is used as both value
|
|
581
|
+
and label), or a callable invoked at render time. The callable
|
|
582
|
+
may take no argument (``fn()``) for a static list, or ``(field)``
|
|
583
|
+
to adapt the suggestions to the current field value at each
|
|
584
|
+
render — convenient for server-side filtering::
|
|
585
|
+
|
|
586
|
+
DataList(lambda field: search(field.data))
|
|
587
|
+
|
|
588
|
+
**Inline DataList owned by a field**
|
|
589
|
+
|
|
590
|
+
Pass a :class:`DataList` instance directly as ``datalist=`` to get
|
|
591
|
+
a field-owned datalist with a unique id derived from the field's
|
|
592
|
+
id::
|
|
593
|
+
|
|
594
|
+
class F(Form):
|
|
595
|
+
country = StringField(datalist=DataList(["FR", "US"]))
|
|
596
|
+
|
|
597
|
+
{{ form.country() }}
|
|
598
|
+
{{ form.country.datalist() }}
|
|
599
|
+
|
|
600
|
+
``{{ form.country() }}`` emits the ``<input list="...">`` and
|
|
601
|
+
``{{ form.country.datalist() }}`` emits the matching
|
|
602
|
+
:mdn-tag:`datalist` element. Both calls are explicit, like
|
|
603
|
+
:attr:`Field.errors` or :attr:`Field.label` — you decide where each
|
|
604
|
+
piece appears in the DOM.
|
|
605
|
+
|
|
606
|
+
Inside a :class:`~wtforms.fields.FieldList`, each entry binds its
|
|
607
|
+
own clone of the inline :class:`DataList`, so a callable
|
|
608
|
+
``choices`` sees each entry's own value::
|
|
609
|
+
|
|
610
|
+
class F(Form):
|
|
611
|
+
items = FieldList(
|
|
612
|
+
StringField(
|
|
613
|
+
datalist=DataList(lambda field: suggest(field.data))
|
|
614
|
+
),
|
|
615
|
+
min_entries=3,
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
{% for entry in form.items %}
|
|
619
|
+
{{ entry() }}
|
|
620
|
+
{{ entry.datalist() }}
|
|
621
|
+
{% endfor %}
|
|
622
|
+
|
|
623
|
+
**String references**
|
|
624
|
+
|
|
625
|
+
A field may also reference an existing datalist id directly by
|
|
626
|
+
string::
|
|
627
|
+
|
|
628
|
+
class F(Form):
|
|
629
|
+
field = StringField(datalist="external")
|
|
630
|
+
|
|
631
|
+
The ``list="external"`` attribute is emitted as-is — the user is
|
|
632
|
+
expected to render the :mdn-tag:`datalist` element themselves (in
|
|
633
|
+
a shared layout, a static template, or another form on the same
|
|
634
|
+
page). ``field.datalist()`` returns empty markup in this case, so
|
|
635
|
+
calling it unconditionally in templates is safe.
|
|
636
|
+
|
|
637
|
+
**Compatible fields**
|
|
638
|
+
|
|
639
|
+
Any field accepts a ``datalist=`` parameter, but only text-based
|
|
640
|
+
HTML inputs (``text``, ``search``, ``email``, ``tel``, ``url``,
|
|
641
|
+
``number``, ``range``, ``color``, date/time inputs) honor the
|
|
642
|
+
``list=`` attribute. Passing ``datalist=`` to a
|
|
643
|
+
:class:`~wtforms.fields.SelectField` or
|
|
644
|
+
:class:`~wtforms.fields.TextAreaField` is silently ignored by the
|
|
645
|
+
browser.
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
.. currentmodule:: wtforms.fields
|
|
649
|
+
|
|
473
650
|
Custom Fields
|
|
474
651
|
-------------
|
|
475
652
|
|
|
@@ -583,8 +760,7 @@ Additional Helper Classes
|
|
|
583
760
|
.. class:: Label
|
|
584
761
|
|
|
585
762
|
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>``
|
|
763
|
+
Labels can be printed to yield a :mdn-tag:`label`
|
|
588
764
|
HTML tag enclosure. Similar to fields, you can also call the label with
|
|
589
765
|
additional html params.
|
|
590
766
|
|
|
@@ -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.
|