form.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. import itertools
  2. from collections import OrderedDict
  3. from wtforms.meta import DefaultMeta
  4. from wtforms.utils import unset_value
  5. __all__ = ("BaseForm", "Form")
  6. _default_meta = DefaultMeta()
  7. class BaseForm:
  8. """
  9. Base Form Class. Provides core behaviour like field construction,
  10. validation, and data and error proxying.
  11. """
  12. def __init__(self, fields, prefix="", meta=_default_meta):
  13. """
  14. :param fields:
  15. A dict or sequence of 2-tuples of partially-constructed fields.
  16. :param prefix:
  17. If provided, all fields will have their name prefixed with the
  18. value.
  19. :param meta:
  20. A meta instance which is used for configuration and customization
  21. of WTForms behaviors.
  22. """
  23. if prefix and prefix[-1] not in "-_;:/.":
  24. prefix += "-"
  25. self.meta = meta
  26. self._form_error_key = ""
  27. self._prefix = prefix
  28. self._fields = OrderedDict()
  29. if hasattr(fields, "items"):
  30. fields = fields.items()
  31. translations = self.meta.get_translations(self)
  32. extra_fields = []
  33. if meta.csrf:
  34. self._csrf = meta.build_csrf(self)
  35. extra_fields.extend(self._csrf.setup_form(self))
  36. for name, unbound_field in itertools.chain(fields, extra_fields):
  37. field_name = unbound_field.name or name
  38. options = dict(name=field_name, prefix=prefix, translations=translations)
  39. field = meta.bind_field(self, unbound_field, options)
  40. self._fields[name] = field
  41. self.form_errors = []
  42. def __iter__(self):
  43. """Iterate form fields in creation order."""
  44. return iter(self._fields.values())
  45. def __contains__(self, name):
  46. """Returns `True` if the named field is a member of this form."""
  47. return name in self._fields
  48. def __getitem__(self, name):
  49. """Dict-style access to this form's fields."""
  50. return self._fields[name]
  51. def __setitem__(self, name, value):
  52. """Bind a field to this form."""
  53. self._fields[name] = value.bind(form=self, name=name, prefix=self._prefix)
  54. def __delitem__(self, name):
  55. """Remove a field from this form."""
  56. del self._fields[name]
  57. def populate_obj(self, obj):
  58. """
  59. Populates the attributes of the passed `obj` with data from the form's
  60. fields.
  61. :note: This is a destructive operation; Any attribute with the same name
  62. as a field will be overridden. Use with caution.
  63. """
  64. for name, field in self._fields.items():
  65. field.populate_obj(obj, name)
  66. def process(self, formdata=None, obj=None, data=None, extra_filters=None, **kwargs):
  67. """Process default and input data with each field.
  68. :param formdata: Input data coming from the client, usually
  69. ``request.form`` or equivalent. Should provide a "multi
  70. dict" interface to get a list of values for a given key,
  71. such as what Werkzeug, Django, and WebOb provide.
  72. :param obj: Take existing data from attributes on this object
  73. matching form field attributes. Only used if ``formdata`` is
  74. not passed.
  75. :param data: Take existing data from keys in this dict matching
  76. form field attributes. ``obj`` takes precedence if it also
  77. has a matching attribute. Only used if ``formdata`` is not
  78. passed.
  79. :param extra_filters: A dict mapping field attribute names to
  80. lists of extra filter functions to run. Extra filters run
  81. after filters passed when creating the field. If the form
  82. has ``filter_<fieldname>``, it is the last extra filter.
  83. :param kwargs: Merged with ``data`` to allow passing existing
  84. data as parameters. Overwrites any duplicate keys in
  85. ``data``. Only used if ``formdata`` is not passed.
  86. """
  87. formdata = self.meta.wrap_formdata(self, formdata)
  88. if data is not None:
  89. kwargs = dict(data, **kwargs)
  90. filters = extra_filters.copy() if extra_filters is not None else {}
  91. for name, field in self._fields.items():
  92. field_extra_filters = filters.get(name, [])
  93. inline_filter = getattr(self, f"filter_{name}", None)
  94. if inline_filter is not None:
  95. field_extra_filters.append(inline_filter)
  96. if obj is not None and hasattr(obj, name):
  97. data = getattr(obj, name)
  98. elif name in kwargs:
  99. data = kwargs[name]
  100. else:
  101. data = unset_value
  102. field.process(formdata, data, extra_filters=field_extra_filters)
  103. def validate(self, extra_validators=None):
  104. """
  105. Validates the form by calling `validate` on each field.
  106. :param extra_validators:
  107. If provided, is a dict mapping field names to a sequence of
  108. callables which will be passed as extra validators to the field's
  109. `validate` method.
  110. Returns `True` if no errors occur.
  111. """
  112. success = True
  113. for name, field in self._fields.items():
  114. if extra_validators is not None and name in extra_validators:
  115. extra = extra_validators[name]
  116. else:
  117. extra = tuple()
  118. if not field.validate(self, extra):
  119. success = False
  120. return success
  121. @property
  122. def data(self):
  123. return {name: f.data for name, f in self._fields.items()}
  124. @property
  125. def errors(self):
  126. errors = {name: f.errors for name, f in self._fields.items() if f.errors}
  127. if self.form_errors:
  128. errors[self._form_error_key] = self.form_errors
  129. return errors
  130. class FormMeta(type):
  131. """
  132. The metaclass for `Form` and any subclasses of `Form`.
  133. `FormMeta`'s responsibility is to create the `_unbound_fields` list, which
  134. is a list of `UnboundField` instances sorted by their order of
  135. instantiation. The list is created at the first instantiation of the form.
  136. If any fields are added/removed from the form, the list is cleared to be
  137. re-generated on the next instantiation.
  138. Any properties which begin with an underscore or are not `UnboundField`
  139. instances are ignored by the metaclass.
  140. """
  141. def __init__(cls, name, bases, attrs):
  142. type.__init__(cls, name, bases, attrs)
  143. cls._unbound_fields = None
  144. cls._wtforms_meta = None
  145. def __call__(cls, *args, **kwargs):
  146. """
  147. Construct a new `Form` instance.
  148. Creates the `_unbound_fields` list and the internal `_wtforms_meta`
  149. subclass of the class Meta in order to allow a proper inheritance
  150. hierarchy.
  151. """
  152. if cls._unbound_fields is None:
  153. fields = []
  154. for name in dir(cls):
  155. if not name.startswith("_"):
  156. unbound_field = getattr(cls, name)
  157. if hasattr(unbound_field, "_formfield"):
  158. fields.append((name, unbound_field))
  159. # We keep the name as the second element of the sort
  160. # to ensure a stable sort.
  161. fields.sort(key=lambda x: (x[1].creation_counter, x[0]))
  162. cls._unbound_fields = fields
  163. # Create a subclass of the 'class Meta' using all the ancestors.
  164. if cls._wtforms_meta is None:
  165. bases = []
  166. for mro_class in cls.__mro__:
  167. if "Meta" in mro_class.__dict__:
  168. bases.append(mro_class.Meta)
  169. cls._wtforms_meta = type("Meta", tuple(bases), {})
  170. return type.__call__(cls, *args, **kwargs)
  171. def __setattr__(cls, name, value):
  172. """
  173. Add an attribute to the class, clearing `_unbound_fields` if needed.
  174. """
  175. if name == "Meta":
  176. cls._wtforms_meta = None
  177. elif not name.startswith("_") and hasattr(value, "_formfield"):
  178. cls._unbound_fields = None
  179. type.__setattr__(cls, name, value)
  180. def __delattr__(cls, name):
  181. """
  182. Remove an attribute from the class, clearing `_unbound_fields` if
  183. needed.
  184. """
  185. if not name.startswith("_"):
  186. cls._unbound_fields = None
  187. type.__delattr__(cls, name)
  188. class Form(BaseForm, metaclass=FormMeta):
  189. """
  190. Declarative Form base class. Extends BaseForm's core behaviour allowing
  191. fields to be defined on Form subclasses as class attributes.
  192. In addition, form and instance input data are taken at construction time
  193. and passed to `process()`.
  194. """
  195. Meta = DefaultMeta
  196. def __init__(
  197. self,
  198. formdata=None,
  199. obj=None,
  200. prefix="",
  201. data=None,
  202. meta=None,
  203. **kwargs,
  204. ):
  205. """
  206. :param formdata: Input data coming from the client, usually
  207. ``request.form`` or equivalent. Should provide a "multi
  208. dict" interface to get a list of values for a given key,
  209. such as what Werkzeug, Django, and WebOb provide.
  210. :param obj: Take existing data from attributes on this object
  211. matching form field attributes. Only used if ``formdata`` is
  212. not passed.
  213. :param prefix: If provided, all fields will have their name
  214. prefixed with the value. This is for distinguishing multiple
  215. forms on a single page. This only affects the HTML name for
  216. matching input data, not the Python name for matching
  217. existing data.
  218. :param data: Take existing data from keys in this dict matching
  219. form field attributes. ``obj`` takes precedence if it also
  220. has a matching attribute. Only used if ``formdata`` is not
  221. passed.
  222. :param meta: A dict of attributes to override on this form's
  223. :attr:`meta` instance.
  224. :param extra_filters: A dict mapping field attribute names to
  225. lists of extra filter functions to run. Extra filters run
  226. after filters passed when creating the field. If the form
  227. has ``filter_<fieldname>``, it is the last extra filter.
  228. :param kwargs: Merged with ``data`` to allow passing existing
  229. data as parameters. Overwrites any duplicate keys in
  230. ``data``. Only used if ``formdata`` is not passed.
  231. """
  232. meta_obj = self._wtforms_meta()
  233. if meta is not None and isinstance(meta, dict):
  234. meta_obj.update_values(meta)
  235. super().__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)
  236. for name, field in self._fields.items():
  237. # Set all the fields to attributes so that they obscure the class
  238. # attributes with the same names.
  239. setattr(self, name, field)
  240. self.process(formdata, obj, data=data, **kwargs)
  241. def __setitem__(self, name, value):
  242. raise TypeError("Fields may not be added to Form instances, only classes.")
  243. def __delitem__(self, name):
  244. del self._fields[name]
  245. setattr(self, name, None)
  246. def __delattr__(self, name):
  247. if name in self._fields:
  248. self.__delitem__(name)
  249. else:
  250. # This is done for idempotency, if we have a name which is a field,
  251. # we want to mask it by setting the value to None.
  252. unbound_field = getattr(self.__class__, name, None)
  253. if unbound_field is not None and hasattr(unbound_field, "_formfield"):
  254. setattr(self, name, None)
  255. else:
  256. super().__delattr__(name)
  257. def validate(self, extra_validators=None):
  258. """Validate the form by calling ``validate`` on each field.
  259. Returns ``True`` if validation passes.
  260. If the form defines a ``validate_<fieldname>`` method, it is
  261. appended as an extra validator for the field's ``validate``.
  262. :param extra_validators: A dict mapping field names to lists of
  263. extra validator methods to run. Extra validators run after
  264. validators passed when creating the field. If the form has
  265. ``validate_<fieldname>``, it is the last extra validator.
  266. """
  267. if extra_validators is not None:
  268. extra = extra_validators.copy()
  269. else:
  270. extra = {}
  271. for name in self._fields:
  272. inline = getattr(self.__class__, f"validate_{name}", None)
  273. if inline is not None:
  274. extra.setdefault(name, []).append(inline)
  275. return super().validate(extra)