1from wtforms.utils import unset_value
2
3from .. import widgets
4from .core import Field
5
6__all__ = ("FormField",)
7
8
9class FormField(Field):
10 """
11 Encapsulate a form as a field in another form.
12
13 :param form_class:
14 A subclass of Form that will be encapsulated.
15 :param separator:
16 A string which will be suffixed to this field's name to create the
17 prefix to enclosed fields. The default is fine for most uses.
18 """
19
20 widget = widgets.TableWidget()
21
22 def __init__(
23 self, form_class, label=None, validators=None, separator="-", **kwargs
24 ):
25 super().__init__(label, validators, **kwargs)
26 self.form_class = form_class
27 self.separator = separator
28 self._obj = None
29 if self.filters:
30 raise TypeError(
31 "FormField cannot take filters, as the encapsulated"
32 " data is not mutable."
33 )
34 if validators:
35 raise TypeError(
36 "FormField does not accept any validators. Instead,"
37 " define them on the enclosed form."
38 )
39
40 def process(self, formdata, data=unset_value, extra_filters=None):
41 if extra_filters:
42 raise TypeError(
43 "FormField cannot take filters, as the encapsulated"
44 "data is not mutable."
45 )
46
47 if data is unset_value:
48 try:
49 data = self.default()
50 except TypeError:
51 data = self.default
52 self._obj = data
53
54 self.object_data = data
55
56 prefix = self.name + self.separator
57 if isinstance(data, dict):
58 self.form = self.form_class(formdata=formdata, prefix=prefix, **data)
59 else:
60 self.form = self.form_class(formdata=formdata, obj=data, prefix=prefix)
61
62 def validate(self, form, extra_validators=()):
63 if extra_validators:
64 raise TypeError(
65 "FormField does not accept in-line validators, as it"
66 " gets errors from the enclosed form."
67 )
68 return self.form.validate()
69
70 def populate_obj(self, obj, name):
71 candidate = getattr(obj, name, None)
72 if candidate is None:
73 if self._obj is None:
74 raise TypeError(
75 "populate_obj: cannot find a value to populate from"
76 " the provided obj or input data/defaults"
77 )
78 candidate = self._obj
79
80 self.form.populate_obj(candidate)
81 setattr(obj, name, candidate)
82
83 def __iter__(self):
84 return iter(self.form)
85
86 def __getitem__(self, name):
87 return self.form[name]
88
89 def __getattr__(self, name):
90 return getattr(self.form, name)
91
92 @property
93 def data(self):
94 return self.form.data
95
96 @property
97 def errors(self):
98 return self.form.errors