You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
"description": "Custom add and edit forms in Plone"
5
+
"property=og:description": "Custom add and edit forms in Plone"
6
+
"property=og:title": "Custom add and edit forms in Plone"
7
+
"keywords": "Plone, custom, add, edit, forms"
8
8
---
9
9
10
10
# Custom add and edit forms
11
11
12
-
**Using \`z3c.form\`\_ to build custom forms**
12
+
This chapter describes how to use `z3c.form` to build custom forms.
13
13
14
-
Until now, we have used Dexterity’s default content add and edit forms,
15
-
supplying form hints in our schemata to influence how the forms are
16
-
built.
17
-
For most types, that is all that’s ever needed.
18
-
In some cases, however, we want to build custom forms, or supply additional
19
-
forms.
14
+
Until now, we have used Dexterity's default content add and edit forms, supplying form hints in our schemata to influence how the forms are built.
15
+
For most types, that is all that's ever needed.
16
+
In some cases, however, we want to build custom forms, or supply additional forms.
20
17
21
-
Dexterity uses the [z3c.form] library to build its forms, via the
22
-
[plone.z3cform] integration package.
18
+
Dexterity uses the [`z3c.form`](https://z3cform.readthedocs.io/en/latest/) library to build its forms, via the [`plone.z3cform`](https://pypi.org/project/plone.z3cform/) integration package.
23
19
24
-
Dexterity also relies on [plone.autoform], in particular its
25
-
`AutoExtensibleForm` base class, which is responsible for processing
26
-
form hints and setting up [z3c.form] widgets and groups (fieldsets).
27
-
A custom form, therefore, is simply a view that uses these libraries,
28
-
although Dexterity provides some helpful base classes that make it
29
-
easier to construct forms based on the schema and behaviors of a
30
-
Dexterity type.
20
+
Dexterity also relies on [`plone.autoform`](https://pypi.org/project/plone.autoform/), in particular its `AutoExtensibleForm` base class, which is responsible for processing form hints and setting up `z3c.form` widgets and groups (fieldsets).
21
+
A custom form, therefore, is simply a view that uses these libraries, although Dexterity provides some helpful base classes that make it easier to construct forms based on the schema and behaviors of a Dexterity type.
22
+
23
+
```{note}
24
+
If you want to build standalone forms not related to content objects, see the [`z3c.form` documentation](https://z3cform.readthedocs.io/en/latest/).
25
+
```
31
26
32
-
:::{note}
33
-
If you want to build standalone forms not related to content objects,
34
-
see the [z3c.form] documentation.
35
-
:::
36
27
37
28
## Edit forms
38
29
39
-
An edit form is just a form that is registered for a particular type of
40
-
content and knows how to register its fields.
41
-
If the form is named `edit`, it will replace the default edit form,
42
-
which is registered with that name for the more general
43
-
`IDexterityContent` interface.
30
+
An edit form is just a form that is registered for a particular type of content and knows how to register its fields.
31
+
If the form is named `edit`, it will replace the default edit form, which is registered with that name for the more general `IDexterityContent` interface.
44
32
45
-
Dexterity provides a standard edit form base class that provides
46
-
sensible defaults for buttons, labels and so on.
33
+
Dexterity provides a standard edit form base class that provides sensible defaults for buttons, labels, and so on.
47
34
This should be registered for a type schema (not a class).
48
-
To create an edit form that is identical to the default, we could do:
35
+
To create an edit form that is identical to the default, we could do the following.
49
36
50
37
```python
51
38
from plone.dexterity.browser import edit
@@ -54,7 +41,7 @@ class EditForm(edit.DefaultEditForm):
54
41
pass
55
42
```
56
43
57
-
and register it in configure.zcml:
44
+
And register it in {file}`configure.zcml`.
58
45
59
46
```xml
60
47
<browser:page
@@ -65,105 +52,80 @@ and register it in configure.zcml:
65
52
/>
66
53
```
67
54
68
-
This form is of course not terribly interesting, since it is identical
69
-
to the default. However, we can now start changing fields and values.
70
-
For example, we could:
71
-
72
-
- Override the `schema` property to tell [plone.autoform] to use a
73
-
different schema interface (with different form hints) than the
74
-
content type schema.
75
-
- Override the `additionalSchemata` property to tell [plone.autoform]
76
-
to use different supplemental schema interfaces.
77
-
The default is to use all behavior interfaces that provide the
78
-
`IFormFieldProvider` marker from [plone.autoform].
79
-
- Override the `label` and `description` properties to provide
80
-
different a different title and description for the form.
81
-
- Set the [z3c.form]`fields` and `groups` attributes directly.
82
-
- Override the `updateWidgets()` method to modify widget properties,
83
-
or one of the other `update()` methods,
84
-
to perform additional processing on the fields.
85
-
In most cases, these require us to call the `super` version at the
86
-
beginning.
87
-
See the [plone.autoform] and [z3c.form] documentation
88
-
to learn more about the sequence of calls that emanate from the form
89
-
`update()` method in the `z3c.form.form.BaseForm` class.
90
-
- Override the `template` attribute to specify a custom template.
55
+
This form is of course not terribly interesting, since it is identical to the default.
56
+
However, we can now start changing fields and values.
57
+
For example, we could do any of the following.
58
+
59
+
- Override the `schema` property to tell `plone.autoform` to use a different schema interface (with different form hints) than the content type schema.
60
+
- Override the `additionalSchemata` property to tell `plone.autoform` to use different supplemental schema interfaces.
61
+
The default is to use all behavior interfaces that provide the `IFormFieldProvider` marker from `plone.autoform`.
62
+
- Override the `label` and `description` properties to provide a different title and description for the form.
63
+
- Set the `z3c.form``fields` and `groups` attributes directly.
64
+
- Override the `updateWidgets()` method to modify widget properties, or one of the other `update()` methods, to perform additional processing on the fields.
65
+
In most cases, these require us to call the `super` version at the beginning.
66
+
See the [`plone.autoform`](https://pypi.org/project/plone.autoform/#introduction) and [`z3c.form` documentation](https://z3cform.readthedocs.io/en/latest/) to learn more about the sequence of calls that emanate from the form `update()` method in the `z3c.form.form.BaseForm` class.
67
+
- Override the `template` attribute to specify a custom template.
68
+
91
69
92
70
## Content add sequence
93
71
94
-
Add forms are similar to edit forms in that they are built from a type’s
95
-
schema and the schemata of its behaviors.
96
-
However, for an add form to be able to construct a content object,
97
-
it needs to know which `portal_type` to use.
72
+
Add forms are similar to edit forms in that they are built from a type's schema and the schemata of its behaviors.
73
+
However, for an add form to be able to construct a content object, it needs to know which `portal_type` to use.
98
74
99
-
You should realise that the FTIs in the `portal_types` tool can be
100
-
modified through the web.
101
-
It is even possible to create new types through the web that re-use existing
102
-
classes and factories.
75
+
You should realize that the FTIs in the `portal_types` tool can be modified through the web.
76
+
It is even possible to create new types through the web that reuse existing classes and factories.
103
77
104
-
For this reason, add forms are looked up via a namespace traversal
105
-
adapter called `++add++`.
78
+
For this reason, add forms are looked up via a namespace traversal adapter called `++add++`.
106
79
You may have noticed this in the URLs to add forms already.
107
-
What actually happens is this:
108
-
109
-
- Plone renders the {guilabel}`add` menu.
110
-
111
-
- To do so, it looks, among other places, for actions in the *folder/add* category. This category is provided by the `portal_types` tool.
112
-
- The *folder/add* action category is constructed by looking up the `add_view_expr` property on the FTIs of all addable types. This is a TALES expression telling the add menu which URL to use.
113
-
- The default `add_view_expr` in Dexterity (and CMF 2.2) is `string:${folder_url}/++add++${fti/getId}`. That is, it uses the `++add++` traversal namespace with an argument containing the FTI name.
114
-
115
-
- A user clicks on an entry in the menu and is taken to a URL like `/path/to/folder/++add++my.type`.
116
-
117
-
> - The `++add++` namespace adapter looks up the FTI with the given name, and gets its `factory` property.
118
-
> - The `factory` property of an FTI gives the name of a particular `zope.component.interfaces.IFactory` utility, which is used later to construct an instance of the content object. Dexterity automatically registers a factory instance for each type, with a name that matches the type name, although it is possible to use an existing factory name in a new type.
119
-
> This allows administrators to create new “logical” types that are
120
-
> functionally identical to an existing type.
121
-
> - The `++add++` namespace adapter looks up the actual form to render as
122
-
> a multi-adapter from `(context, request, fti`) to `Interface` with
123
-
> a name matching the `factory` property.
124
-
> Recall that a standard view is a multi-adapter from
125
-
> `(context, request)` to `Interface` with a name matching the URL
126
-
> segment for which the view is looked up.
127
-
> As such, add forms are not standard views, because they get the
128
-
> additional `fti` parameter when constructed.
129
-
> - If this fails, there is no custom add form for this factory (as is
130
-
> normally the case).
131
-
> The fallback is an unnamed adapter from `(context, request, fti)`.
132
-
> The default Dexterity add form is registered as such an adapter,
133
-
> specific to the `IDexterityFTI` interface.
134
-
135
-
- The form is rendered like any other `z3c.form` form instance,
136
-
and is subject to validation,
137
-
which may cause it to be loaded several times.
138
-
139
-
- Eventually, the form is successfully submitted. At this point:
140
-
141
-
> - The standard `AddForm` base class will look up the factory from the FTI reference it holds and call it to create an instance.
142
-
> - The default Dexterity factory looks at the `klass`[^id2] attribute of the FTI to determine the actual content class to use, creates an object and initialises it.
143
-
> - The `portal_type` attribute of the newly created instance is set to
144
-
> the name of the FTI.
145
-
> Thus, if the FTI is a “logical type” created through the web, but
146
-
> using an existing factory, the new instance’s `portal_type` will be
147
-
> set to the “logical type”.
148
-
> - The object is initialised with the values submitted in the form.
149
-
> - An `IObjectCreatedEvent` is fired.
150
-
> - The object is added to its container.
151
-
> - The user is redirected to the view specified in the `immediate_view`
152
-
> property of the FTI.
80
+
What actually happens is the following.
81
+
82
+
- Plone renders the {guilabel}`add` menu.
83
+
84
+
- To do so, it looks, among other places, for actions in the `folder/add` category.
85
+
This category is provided by the `portal_types` tool.
86
+
- The `folder/add` action category is constructed by looking up the `add_view_expr` property on the FTIs of all addable types.
87
+
This is a TALES expression telling the add menu which URL to use.
88
+
- The default `add_view_expr` in Dexterity (and CMF 2.2) is `string:${folder_url}/++add++${fti/getId}`.
89
+
That is, it uses the `++add++` traversal namespace with an argument containing the FTI name.
90
+
91
+
- A user clicks on an entry in the menu, and is taken to a URL using the parttern `/path/to/folder/++add++my.type`.
92
+
93
+
- The `++add++` namespace adapter looks up the FTI with the given name, and gets its `factory` property.
94
+
- The `factory` property of an FTI gives the name of a particular `zope.component.interfaces.IFactory` utility, which is used later to construct an instance of the content object.
95
+
Dexterity automatically registers a factory instance for each type, with a name that matches the type name, although it is possible to use an existing factory name in a new type.
96
+
This allows administrators to create new "logical" types that are functionally identical to an existing type.
97
+
- The `++add++` namespace adapter looks up the actual form to render as a multi-adapter from `(context, request, fti)` to `Interface` with a name matching the `factory` property.
98
+
Recall that a standard view is a multi-adapter from `(context, request)` to `Interface` with a name matching the URL segment for which the view is looked up.
99
+
As such, add forms are not standard views, because they get the additional `fti` parameter when constructed.
100
+
- If this fails, there is no custom add form for this factory, as is normally the case.
101
+
The fallback is an unnamed adapter from `(context, request, fti)`.
102
+
The default Dexterity add form is registered as such an adapter, specific to the `IDexterityFTI` interface.
103
+
104
+
- The form is rendered like any other `z3c.form` form instance, and is subject to validation, which may cause it to be loaded several times.
105
+
106
+
- Eventually, the form is successfully submitted.
107
+
At this point:
108
+
109
+
- The standard `AddForm` base class will look up the factory from the FTI reference it holds and call it to create an instance.
110
+
- The default Dexterity factory looks at the `klass`[^id2] attribute of the FTI to determine the actual content class to use, creates an object and initializes it.
111
+
- The `portal_type` attribute of the newly created instance is set to the name of the FTI.
112
+
Thus, if the FTI is a "logical type" created through the web, but using an existing factory, the new instance's `portal_type` will be set to the "logical type".
113
+
- The object is initialized with the values submitted in the form.
114
+
- An `IObjectCreatedEvent` is fired.
115
+
- The object is added to its container.
116
+
- The user is redirected to the view specified in the `immediate_view` property of the FTI.
153
117
154
118
[^id2]: `class` is a reserved word in Python, so we use `klass`.
155
119
156
-
This sequence is pretty long, but thankfully we rarely have to worry
157
-
about it. In most cases, we can use the default add form, and when we
158
-
can’t, creating a custom add form is only a bit more difficult than
159
-
creating a custom edit form.
120
+
This sequence is pretty long, but thankfully we rarely have to worry about it.
121
+
In most cases, we can use the default add form, and when we can't, creating a custom add form is only a bit more difficult than creating a custom edit form.
122
+
160
123
161
124
## Custom add forms
162
125
163
-
As with edit forms, Dexterity provides a sensible base class for add
164
-
forms that knows how to deal with the Dexterity FTI and factory.
126
+
As with edit forms, Dexterity provides a sensible base class for add forms that knows how to deal with the Dexterity FTI and factory.
165
127
166
-
A custom form replicating the default would look like this:
128
+
A custom form replicating the default would be the following.
167
129
168
130
```python
169
131
from plone.dexterity.browser import add
@@ -175,7 +137,7 @@ class AddView(add.DefaultAddView):
175
137
form = AddForm
176
138
```
177
139
178
-
and be registered in ZCML like this:
140
+
And be registered in ZCML as follows.
179
141
180
142
```xml
181
143
<adapter
@@ -196,29 +158,16 @@ and be registered in ZCML like this:
196
158
197
159
The name here should match the *factory* name.
198
160
By default, Dexterity types have a factory called the same as the FTI name.
199
-
If no such factory exists
200
-
(i.e. you have not registered a custom `IFactory` utility),
201
-
a local factory utility will be created and managed by Dexterity when the
202
-
FTI is installed.
161
+
If no such factory exists (in other words, you have not registered a custom `IFactory` utility), a local factory utility will be created and managed by Dexterity when the FTI is installed.
203
162
204
163
Also note that we do not specify a context here.
205
164
Add forms are always registered for any `IFolderish` context.
206
165
207
-
:::{note}
208
-
If the permission used for the add form is different to the
209
-
`add_permission` set in the FTI, the user needs to have *both*
210
-
permissions to be able to see the form and add content.
211
-
For this reason, most add forms will use the generic
212
-
`cmf.AddPortalContent` permission.
213
-
The {guilabel}`add` menu will not render links to types where the user
214
-
does not have the add permission stated in the FTI,
215
-
even if this is different to `cmf.AddPortalContent`.
216
-
:::
217
-
218
-
As with edit forms, we can customise this form by overriding [z3c.form]
219
-
and [plone.autoform] properties and methods.
220
-
See the [z3c.form] documentation on add forms for more details.
If the permission used for the add form is different from the `add_permission` set in the FTI, the user needs to have *both* permissions to be able to see the form and add content.
168
+
For this reason, most add forms will use the generic `cmf.AddPortalContent` permission.
169
+
The {guilabel}`add` menu will not render links to types where the user does not have the add permission stated in the FTI, even if this is different to `cmf.AddPortalContent`.
170
+
```
171
+
172
+
As with edit forms, we can customize this form by overriding `z3c.form` and `plone.autoform` properties and methods.
173
+
See the [`z3c.form` documentation](https://z3cform.readthedocs.io/en/latest/) on add forms for more details.
0 commit comments