Dexterity: Reference#
This chapter documents all types of fields, widgets, directives that you can use with dexterity.
Fields included in Plone#
This is a schema with examples for all field-types that are shipped with Plone by default. They are arranged in fieldsets:
- Default
Textline, Text, Boolean, Richtext (html), Email
- Number fields
Integer, Float
- Date and time fields
Datetime, Date, Time, Timedelta
- Choice and Multiple Choice fields
Choice, Choice with radio widget, Choice with Select2 widget, Choice with named vocabulary, List, List with checkboxes, List with Select2 widget, List with values from a named vocabulary but open to additions, Tuple, Set, Set with checkboxes
- Relation fields
Relationchoice, Relationlist
- File fields
File, Image
- Other fields
Uri, Sourcetext, Ascii, Bytesline, Asciiline, Pythonidentifier, Dottedname, Dict, Dict with Choice
Warning
In Volto not all field types and features are implemented yet:
Time, Timedelta, Dict are not supported yet.
Using a callable for basePath in relationfields is not supported yet.
The schema-hints to assign a different widget do not work yet.
1from plone.app.multilingual.browser.interfaces import make_relation_root_path
2from plone.app.textfield import RichText
3from plone.app.z3cform.widget import AjaxSelectFieldWidget
4from plone.app.z3cform.widget import RelatedItemsFieldWidget
5from plone.app.z3cform.widget import SelectFieldWidget
6from plone.autoform import directives
7from plone.dexterity.content import Container
8from plone.namedfile.field import NamedBlobFile
9from plone.namedfile.field import NamedBlobImage
10from plone.schema.email import Email
11from plone.supermodel import model
12from plone.supermodel.directives import fieldset
13from plone.supermodel.directives import primary
14from z3c.form.browser.checkbox import CheckBoxFieldWidget
15from z3c.form.browser.radio import RadioFieldWidget
16from z3c.relationfield.schema import Relation
17from z3c.relationfield.schema import RelationChoice
18from z3c.relationfield.schema import RelationList
19from zope import schema
20from zope.interface import implementer
21
22
23class IExample(model.Schema):
24 """Dexterity-Schema with all field-types."""
25
26 # The most used fields
27 # textline, text, bool, richtext, email
28
29 fieldset(
30 'numberfields',
31 label='Number fields',
32 fields=('int_field', 'float_field'),
33 )
34
35 fieldset(
36 'datetimefields',
37 label='Date and time fields',
38 fields=('datetime_field', 'date_field', 'time_field', 'timedelta_field'),
39 )
40
41 fieldset(
42 'choicefields',
43 label='Choice and Multiple Choice fields',
44 fields=(
45 'choice_field',
46 'choice_field_radio',
47 'choice_field_select',
48 'choice_field_voc',
49 'list_field',
50 'list_field_checkbox',
51 'list_field_select',
52 'list_field_voc_unconstrained',
53 'tuple_field',
54 'set_field',
55 'set_field_checkbox',
56 ),
57 )
58
59 fieldset(
60 'relationfields',
61 label='Relation fields',
62 fields=('relationchoice_field', 'relationlist_field'),
63 )
64
65 fieldset(
66 'filefields',
67 label='File fields',
68 fields=('file_field', 'image_field'),
69 )
70
71 fieldset(
72 'otherfields',
73 label='Other fields',
74 fields=(
75 'uri_field',
76 'sourcetext_field',
77 'ascii_field',
78 'bytesline_field',
79 'asciiline_field',
80 'pythonidentifier_field',
81 'dottedname_field',
82 'dict_field',
83 'dict_field_with_choice',
84 ),
85 )
86
87 primary('title')
88 title = schema.TextLine(
89 title='Primary Field (Textline)',
90 required=True,
91 )
92
93 text_field = schema.Text(
94 title='Text Field',
95 required=False,
96 missing_value='',
97 )
98
99 textline_field = schema.TextLine(
100 title='Textline field',
101 description='A simple input field',
102 required=False,
103 )
104
105 bool_field = schema.Bool(
106 title='Boolean field',
107 required=False,
108 )
109
110 choice_field = schema.Choice(
111 title='Choice field',
112 values=['One', 'Two', 'Three'],
113 required=True,
114 )
115
116 directives.widget(choice_field_radio=RadioFieldWidget)
117 choice_field_radio = schema.Choice(
118 title='Choice field with radio boxes',
119 values=['One', 'Two', 'Three'],
120 required=True,
121 )
122
123 choice_field_voc = schema.Choice(
124 title='Choicefield with values from named vocabulary',
125 vocabulary='plone.app.vocabularies.PortalTypes',
126 required=False,
127 )
128
129 directives.widget(choice_field_select=SelectFieldWidget)
130 choice_field_select = schema.Choice(
131 title='Choicefield with select2 widget',
132 vocabulary='plone.app.vocabularies.PortalTypes',
133 required=False,
134 )
135
136 list_field = schema.List(
137 title='List field',
138 value_type=schema.Choice(
139 values=['Beginner', 'Advanced', 'Professional'],
140 ),
141 required=False,
142 missing_value=[],
143 )
144
145 directives.widget(list_field_checkbox=CheckBoxFieldWidget)
146 list_field_checkbox = schema.List(
147 title='List field with checkboxes',
148 value_type=schema.Choice(
149 values=['Beginner', 'Advanced', 'Professional'],
150 ),
151 required=False,
152 missing_value=[],
153 )
154
155 directives.widget(list_field_select=SelectFieldWidget)
156 list_field_select = schema.List(
157 title='List field with select widget',
158 value_type=schema.Choice(
159 values=['Beginner', 'Advanced', 'Professional'],
160 ),
161 required=False,
162 missing_value=[],
163 )
164
165 list_field_voc_unconstrained = schema.List(
166 title='List field with values from vocabulary but not constrained to them.',
167 value_type=schema.TextLine(),
168 required=False,
169 missing_value=[],
170 )
171 directives.widget(
172 'list_field_voc_unconstrained',
173 AjaxSelectFieldWidget,
174 vocabulary='plone.app.vocabularies.Users',
175 )
176
177 tuple_field = schema.Tuple(
178 title='Tuple field',
179 value_type=schema.Choice(
180 values=['Beginner', 'Advanced', 'Professional'],
181 ),
182 required=False,
183 missing_value=(),
184 )
185
186 set_field = schema.Set(
187 title='Set field',
188 value_type=schema.Choice(
189 values=['Beginner', 'Advanced', 'Professional'],
190 ),
191 required=False,
192 missing_value=set(),
193 )
194
195 directives.widget(set_field_checkbox=CheckBoxFieldWidget)
196 set_field_checkbox = schema.Set(
197 title='Set field with checkboxes',
198 value_type=schema.Choice(
199 values=['Beginner', 'Advanced', 'Professional'],
200 ),
201 required=False,
202 missing_value=set(),
203 )
204
205 # File fields
206 image_field = NamedBlobImage(
207 title='Image field',
208 description='A upload field for images',
209 required=False,
210 )
211
212 file_field = NamedBlobFile(
213 title='File field',
214 description='A upload field for files',
215 required=False,
216 )
217
218 # Date and Time fields
219 datetime_field = schema.Datetime(
220 title='Datetime field',
221 description='Uses a date and time picker',
222 required=False,
223 )
224
225 date_field = schema.Date(
226 title='Date field',
227 description='Uses a date picker',
228 required=False,
229 )
230
231 time_field = schema.Time(
232 title='Time field',
233 required=False,
234 )
235
236 timedelta_field = schema.Timedelta(
237 title='Timedelta field',
238 required=False,
239 )
240
241 # Relation Fields
242 relationchoice_field = RelationChoice(
243 title='Relationchoice field',
244 vocabulary='plone.app.vocabularies.Catalog',
245 required=False,
246 )
247 directives.widget(
248 'relationchoice_field',
249 RelatedItemsFieldWidget,
250 pattern_options={
251 'selectableTypes': ['Document'],
252 'basePath': make_relation_root_path,
253 },
254 )
255
256 relationlist_field = RelationList(
257 title='Relationlist Field',
258 default=[],
259 value_type=RelationChoice(vocabulary='plone.app.vocabularies.Catalog'),
260 required=False,
261 missing_value=[],
262 )
263 directives.widget(
264 'relationlist_field',
265 RelatedItemsFieldWidget,
266 pattern_options={
267 'selectableTypes': ['Document'],
268 'basePath': make_relation_root_path,
269 },
270 )
271
272 # Number fields
273 int_field = schema.Int(
274 title='Integer Field (e.g. 12)',
275 description='Allocated (maximum) number of objects',
276 required=False,
277 )
278
279 float_field = schema.Float(
280 title='Float field (e.g. 12.2)',
281 required=False,
282 )
283
284 # Text fields
285 email_field = Email(
286 title='Email field',
287 description='A simple input field for a email',
288 required=False,
289 )
290
291 uri_field = schema.URI(
292 title='URI field',
293 description='A simple input field for a URLs',
294 required=False,
295 )
296
297 richtext_field = RichText(
298 title='RichText field',
299 description='This uses a richtext editor.',
300 max_length=2000,
301 required=False,
302 )
303
304 sourcetext_field = schema.SourceText(
305 title='SourceText field',
306 required=False,
307 )
308
309 ascii_field = schema.ASCII(
310 title='ASCII field',
311 required=False,
312 )
313
314 bytesline_field = schema.BytesLine(
315 title='BytesLine field',
316 required=False,
317 )
318
319 asciiline_field = schema.ASCIILine(
320 title='ASCIILine field',
321 required=False,
322 )
323
324 pythonidentifier_field = schema.PythonIdentifier(
325 title='PythonIdentifier field',
326 required=False,
327 )
328
329 dottedname_field = schema.DottedName(
330 title='DottedName field',
331 required=False,
332 )
333
334 dict_field = schema.Dict(
335 title='Dict field',
336 required=False,
337 key_type=schema.TextLine(
338 title='Key',
339 required=False,
340 ),
341 value_type=schema.TextLine(
342 title='Value',
343 required=False,
344 ),
345 )
346
347 dict_field_with_choice = schema.Dict(
348 title='Dict field with key and value as choice',
349 required=False,
350 key_type=schema.Choice(
351 title='Key',
352 values=['One', 'Two', 'Three'],
353 required=False,
354 ),
355 value_type=schema.Set(
356 title='Value',
357 value_type=schema.Choice(
358 values=['Beginner', 'Advanced', 'Professional'],
359 ),
360 required=False,
361 missing_value={},
362 ),
363 )
364
365
366@implementer(IExample)
367class Example(Container):
368 """Example instance class"""
How fields look like#
Backend#
This is how these fields look like when editing content in the backend:
Frontend#
This is how these fields look like when editing content in Volto:
3rd party fields#
To control the avilable values of other fields or hide/show them based on user input use the Masterselect Field.
For spam-protection use collective.z3cform.norobots.
Color-Picker collective.z3cform.colorpicker
There is no Computedfield but most use-cases can be achieved with a readonly-field and a property. See the discussion
Datagrid Field#
The datagrid field allows you to enter multiple values at once as rows in a table. Each row is a sub form defined in a separate schema.
Note
The datagrid field is for Plone Classic. See the mixedfield below, if you are working with Plone.
Here is an example:
1from collective.z3cform.datagridfield import DataGridFieldFactory
2from collective.z3cform.datagridfield import DictRow
3from plone.app.z3cform.widget import SelectFieldWidget
4from plone.autoform import directives
5from plone.supermodel import model
6from zope import schema
7from zope.interface import Interface
8
9
10class IMyRowSchema(Interface):
11
12 choice_field = schema.Choice(
13 title='Choice Field',
14 vocabulary='plone.app.vocabularies.PortalTypes',
15 required=False,
16 )
17 directives.widget('objective', SelectFieldWidget)
18
19 textline_field = schema.TextLine(
20 title='Textline field',
21 required=False,
22 )
23
24 bool_field = schema.Bool(
25 title='Boolean field',
26 required=False,
27 )
28
29
30class IExampleWithDatagrid(model.Schema):
31
32 title = schema.TextLine(title='Title', required=True)
33
34 datagrid_field = schema.List(
35 title='Datagrid field',
36 value_type=DictRow(title='Table', schema=IMyRowSchema),
37 default=[],
38 required=False,
39 )
40 directives.widget('datagrid_field', DataGridFieldFactory)
The edit-form looks like this:
The output looks like this:
mixedfield#
The mixedfield empowers your user to create a list of objects of mixed value types sharing the same schema. If you are familliar with the Plone Classic datagrid field this is the complementary field / widget combo for Plone. mixedfield is a combination of a Plone Classic JSONField and a widget for Plone. Nothing new, just a term to talk about linking backend and frontend.
Example is a custom history:
Backend#
Add a field history_field to your content type schema.
1MIXEDFIELD_SCHEMA = json.dumps(
2 {
3 'type': 'object',
4 'properties': {'items': {'type': 'array', 'items': {'type': 'object', 'properties': {}}}},
5 }
6)
7
8class IExample(model.Schema):
9 """Dexterity-Schema"""
10
11 fieldset(
12 'datagrid',
13 label='Datagrid field',
14 fields=(
15 # 'datagrid_field',
16 'mixed_field',
17 ),
18 )
19
20 primary('title')
21 title = schema.TextLine(
22 title='Primary Field (Textline)',
23 description='zope.schema.TextLine',
24 required=True,
25 )
26
27 description = schema.TextLine(
28 title='Description (Textline)',
29 description='zope.schema.TextLine',
30 required=False,
31 )
32
33 history_field = JSONField(
34 title='Mixedfield: datagrid field for Plone',
35 required=False,
36 schema=MIXEDFIELD_SCHEMA,
37 widget='history_widget',
38 default={'items': []},
39 missing_value={'items': []},
40 )
Frontend#
Provide a widget in your favorite add-on with a schema of elementary fields you need.
1import React from 'react';
2
3import ObjectListWidget from '@plone/volto/components/manage/Widgets/ObjectListWidget';
4
5const ItemSchema = {
6 title: 'History-Entry',
7 properties: {
8 historydate: {
9 title: 'Date',
10 widget: 'date',
11 },
12 historytopic: {
13 title: 'What',
14 },
15 historyversion: {
16 title: 'Version',
17 },
18 historyauthor: {
19 title: 'Who',
20 },
21 },
22 fieldsets: [
23 {
24 id: 'default',
25 title: 'History-Entry',
26 fields: [
27 'historydate',
28 'historytopic',
29 'historyversion',
30 'historyauthor',
31 ],
32 },
33 ],
34 required: [],
35};
36
37const HistoryWidget = (props) => {
38 return (
39 <ObjectListWidget
40 schema={ItemSchema}
41 {...props}
42 value={props.value?.items || props.default?.items || []}
43 onChange={(id, value) => props.onChange(id, { items: value })}
44 />
45 );
46};
47
48export default HistoryWidget;
Keeping this example as simple as possible we skipped the localization. Please see Volto documentation for details.
Register this widget for the backend field of your choice in your apps configuration config.js
.
The following config code registers the custom Plone HistoryWidget for Plone Classic fields with widget "history_widget".
1import { HistoryWidget } from '@rohberg/voltotestsomevoltothings/components';
2
3// All your imports required for the config here BEFORE this line
4import '@plone/volto/config';
5
6export default function applyConfig(config) {
7 config.settings = {
8 ...config.settings,
9 supportedLanguages: ['en', 'de', 'it'],
10 defaultLanguage: 'en',
11 };
12 config.widgets.widget.history_widget = HistoryWidget;
13
14 return config;
15}
Please be sure to use plone.restapi
version >= 7.3.0. If you cannot upgrade plone.restapi
then a registration per field id instead of a registration per field widget name is needed.
export default function applyConfig(config) {
config.widgets.id.history_field = HistoryWidget;
return config;
}
The user can now edit the values of the new field history_field.
Thats what you did to accomplish this:
You added a new field of type JSONField with widget "history_widget" and default schema to your content type schema.
You registered the custom Plone widget for widget name "history_widget".
A view (ExampleView
) of the content type integrates a component to display the values of the field history_field.
1import React from 'react';
2import moment from 'moment';
3import { Container, Table } from 'semantic-ui-react';
4
5const MyHistory = ({ history }) => {
6 return (
7 _CLIENT__ && (
8 <Table celled className="history_list">
9 <Table.Header>
10 <Table.Row>
11 <Table.HeaderCell>Date</Table.HeaderCell>
12 <Table.HeaderCell>What</Table.HeaderCell>
13 <Table.HeaderCell>Version</Table.HeaderCell>
14 <Table.HeaderCell>Who</Table.HeaderCell>
15 </Table.Row>
16 </Table.Header>
17
18 <Table.Body>
19 {history?.items?.map((item) => (
20 <Table.Row>
21 <Table.Cell>
22 {item.historydate && moment(item.historydate).format('L')}
23 </Table.Cell>
24 <Table.Cell>{item.historytopic}</Table.Cell>
25 <Table.Cell>{item.historyversion}</Table.Cell>
26 <Table.Cell>{item.historyauthor}</Table.Cell>
27 </Table.Row>
28 ))}
29 </Table.Body>
30 </Table>
31 )
32 );
33};
34
35const ExampleView = ({ content }) => {
36 return (
37 <Container>
38 <h2>I am an ExampleView</h2>
39 <h3>History</h3>
40 <MyHistory history={content.history_field} />
41 </Container>
42 );
43 };
44
45 export default ExampleView;
Et voilà.
Widgets#
Todo
Document all available widgets
Directives#
Directives can be placed anywhere in the class body (annotations are made directly on the class). By convention they are kept next to the fields they apply to.
For example, here is a schema that omits a field:
from plone.autoform import directives
from plone.supermodel import model
from zope import schema
class ISampleSchema(model.Schema):
title = schema.TextLine(title='Title')
directives.omitted('additionalInfo')
additionalInfo = schema.Bytes()
You can also handle multiple fields with one directive:
directives.omitted('field_1', 'field_2')
With the directive "mode" you can set fields to 'input', 'display' or 'hidden'.
directives.mode(additionalInfo='hidden')
You can apply directives to certain forms only. Here we drop a field from the add-form, it will still show up in the edit-form.
from z3c.form.interfaces import IAddForm
class ITask(model.Schema):
title = schema.TextLine(title='Title')
directives.omitted(IAddForm, 'done')
done = schema.Bool(
title='Done',
required=False,
)
The same works for custom forms.
With the directive widget()
you can not only change the widget used for a field. With pattern_options
you can pass additional parameters to the widget. Here, we configure the datetime widget powered by the JavaScript library pickadate by adding options that are used by it. Plone passes the options to the library.
class IMeeting(model.Schema):
meeting_date = schema.Datetime(
title='Date and Time',
required=False,
)
directives.widget(
'meeting_date',
DatetimeFieldWidget,
pattern_options={
'time': {'interval': 60, 'min': [7, 0], 'max': [19, 0]}},
)
Validation and default values#
In the following example we add a validator and a default value.
from zope.interface import Invalid
import datetime
def future_date(value):
if value and not value.date() >= datetime.date.today():
raise Invalid('Meeting date can not be before today.')
return True
def meeting_date_default_value():
return datetime.datetime.today() + datetime.timedelta(7)
class IMeeting(model.Schema):
meeting_date = schema.Datetime(
title='Date and Time',
required=False,
constraint=future_date,
defaultFactory=meeting_date_default_value,
)
Validators and defaults can be also be made aware of the context (i.e. to check against the values of other fields).
For context aware defaults you need to use a IContextAwareDefaultFactory
. It will be passed the container for which the add form is being displayed:
from zope.interface import provider
from zope.schema.interfaces import IContextAwareDefaultFactory
@provider(IContextAwareDefaultFactory)
def get_container_id(context):
return context.id.upper()
class IMySchema(model.Schema):
parent_id = schema.TextLine(
title='Parent ID',
required=False,
defaultFactory=get_container_id,
)
For context-aware validators you need to use invariant()
:
from zope.interface import Invalid
from zope.interface import invariant
from zope.schema.interfaces import IContextAwareDefaultFactory
class IMyEvent(model.Schema):
start = schema.Datetime(
title='Start date',
required=False,
)
end = schema.Datetime(
title='End date',
required=False,
)
@invariant
def validate_start_end(data):
if data.start is not None and data.end is not None:
if data.start > data.end:
raise Invalid('Start must be before the end.')
See also
To learn more about directives, validators and default values, refer to the following:
Validation (this documentation unfortunately still uses the obsolete grok technology)