Source code for wtforms.datalist
import warnings
from dataclasses import dataclass
from dataclasses import field
from wtforms import widgets
from wtforms._compat import get_signature
from wtforms.fields.choices import _enum_options
from wtforms.fields.choices import Choice
__all__ = ("DataList", "DataListChoice", "enum_datalist")
[docs]@dataclass
class DataListChoice:
"""
An option declared via :class:`~wtforms.DataList`'s ``choices=``
parameter.
:param value:
The value rendered as the ``<option>``'s ``value`` attribute.
:param label:
The label of the option. Defaults to ``value`` when omitted.
:param render_kw:
A dict containing HTML attributes that will be rendered
with the option. Defaults to an empty dict when omitted.
"""
value: str
label: str | None = None
render_kw: dict = field(default_factory=dict)
def __post_init__(self):
if self.label is None:
self.label = self.value
def __iter__(self):
return iter((self.value, self.label, self.render_kw))
@classmethod
def from_input(cls, input):
"""Coerce a value passed by the user into a :class:`DataListChoice`."""
if isinstance(input, DataListChoice):
return input
if isinstance(input, Choice):
warnings.warn(
"Passing Choice to a DataList is deprecated; Choice is the "
"output type returned by iter_choices(). Use DataListChoice "
"instead. Support for Choice as input will be removed in "
"WTForms 4.0.",
DeprecationWarning,
stacklevel=4,
)
return cls(
value=input.value,
label=input.label,
render_kw=input.render_kw,
)
if isinstance(input, str):
return cls(value=input)
if isinstance(input, tuple):
return cls(*input)
[docs]def enum_datalist(enum_cls, *, by="value", label=None):
"""Build a list of :class:`DataListChoice` from an :class:`enum.Enum` class.
Same semantics as :func:`~wtforms.fields.enum_choices`: ``by`` selects
which member attribute becomes the ``<option>`` value (``"value"`` by
default, ``"name"`` otherwise) and ``label`` defaults to ``str(member)``
when the Enum defines its own ``__str__``, else ``member.name``.
A ``<datalist>`` only suggests values for a free-text field, so there is
no coercion counterpart — the field stores whatever string is submitted.
"""
return _enum_options(enum_cls, by, label, DataListChoice)
[docs]class DataList:
"""A ``<datalist>`` of suggestions attached to a single field.
Passed to a :class:`~wtforms.Field` via its ``datalist=`` parameter
to add an autocomplete-style list of choices. See the WTForms
fields documentation for usage.
"""
widget = widgets.DataListWidget()
def __init__(self, choices=None, *, render_kw=None, widget=None):
self._raw_choices = choices
self._choices = None if callable(choices) else choices
self.render_kw = render_kw or {}
if widget is not None:
self.widget = widget
self.id = None
def _clone(self, id):
clone = DataList.__new__(DataList)
clone._raw_choices = self._raw_choices
clone._choices = None if callable(self._raw_choices) else self._raw_choices
clone.render_kw = self.render_kw
clone.widget = self.widget
clone.id = id
return clone
def _resolve(self, field):
raw = self._raw_choices
if not callable(raw):
return
try:
sig = get_signature(raw)
sig.bind(field._form, field)
except TypeError:
self._choices = raw()
return
self._choices = raw(field._form, field)
def iter_choices(self, field=None):
raw = self._choices
if raw is None:
return []
if isinstance(raw, dict):
return [
DataListChoice(value=value, label=label) for value, label in raw.items()
]
return [DataListChoice.from_input(item) for item in raw]
def __call__(self, field=None, **kwargs):
return self.widget(self, field=field, **kwargs)