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
**Creating your own static and dynamic vocabularies**
12
+
This chapter describes how to create your own static and dynamic vocabularies.
13
13
14
-
Vocabularies are normally used in conjunction with selection fields,
15
-
and are supported by the [zope.schema] package,
16
-
with widgets provided by [z3c.form].
14
+
Vocabularies are normally used in conjunction with selection fields, and are supported by the [`zope.schema`](https://pypi.org/project/zope.schema/) package, with widgets provided by [`z3c.form`](https://pypi.org/project/z3c.form/).
17
15
18
16
Selection fields use the `Choice` field type.
19
-
To allow the user to select a single value,
20
-
use a `Choice` field directly:
17
+
To allow the user to select a single value, use a `Choice` field directly:
21
18
22
19
```python
23
20
classIMySchema(model.Schema):
24
21
myChoice = schema.Choice(...)
25
22
```
26
23
27
-
For a multi-select field, use a `List`, `Tuple`, `Set` or `Frozenset` with a `Choice` as the `value_type`:
24
+
For a multi-select field, use a `List`, `Tuple`, `Set`, or `Frozenset`, with a `Choice` as the `value_type`:
28
25
29
26
```python
30
27
classIMySchema(model.Schema):
31
28
32
29
myList = schema.List(
33
-
...,
34
30
value_type=schema.Choice(...)
35
31
)
36
32
```
37
33
38
-
The choice field must be passed one of the following arguments:
34
+
The `Choice` field must be passed one of the following arguments:
39
35
40
-
-`values` can be used to give a list of static values;
41
-
-`vocabulary` can be used to refer to an `IVocabulary` instance or (more commonly) a string giving the name of an `IVocabularyFactory` named utility.
42
-
-`source` can be used to refer to an `IContextSourceBinder` or `ISource` instance;
36
+
-`values` can be used to give a list of static values.
37
+
-`vocabulary` can be used to refer to an `IVocabulary` instance or (more commonly) a string giving the name of an `IVocabularyFactory` named utility.
38
+
-`source` can be used to refer to an `IContextSourceBinder` or `ISource` instance.
39
+
40
+
In the remainder of this section, we will show the various techniques for defining vocabularies through several iterations of a new field added to the `Program` type, allowing the user to pick the organizer responsible for the program.
43
41
44
-
In the remainder of this section,
45
-
we will show the various techniques for defining vocabularies through several iterations of a new field added to the Program type allowing the user to pick the organiser responsible for the program.
46
42
47
43
## Static vocabularies
48
44
49
-
Our first attempt uses a static list of organisers.
50
-
We use the message factory to allow the labels (term titles) to be translated.
51
-
The values stored in the `organizer` field will be a unicode object representing the chosen label,
52
-
or `None` if no value is selected:
45
+
Our first attempt uses a static list of organizers.
46
+
We use the message factory to allow the labels to be translated.
47
+
The values stored in the `organizer` field will be a Unicode object representing the chosen label, or `None` if no value is selected:
53
48
54
49
```python
55
50
from zope.schema.vocabulary import SimpleTerm
56
51
from zope.schema.vocabulary import SimpleVocabulary
57
52
58
53
organizers = SimpleVocabulary(
59
54
[
60
-
SimpleTerm(value=u'Bill', title=_(u'Bill')),
61
-
SimpleTerm(value=u'Bob', title=_(u'Bob')),
62
-
SimpleTerm(value=u'Jim', title=_(u'Jim'))
55
+
SimpleTerm(value='Bill', title=_('Bill')),
56
+
SimpleTerm(value='Bob', title=_('Bob')),
57
+
SimpleTerm(value='Jim', title=_('Jim'))
63
58
]
64
59
)
65
60
66
61
organizer = schema.Choice(
67
-
title=_(u"Organiser"),
62
+
title=_('organizer"),
68
63
vocabulary=organizers,
69
64
required=False,
70
65
)
71
66
```
72
67
73
68
Since `required`is`False`, there will be a {guilabel}`no value` option in the drop-down list.
74
69
70
+
75
71
## Dynamic sources
76
72
77
-
The static vocabulary is obviously a bit limited,
78
-
since it is hard-coded in Python.
73
+
The static vocabulary is obviously a bit limited, since it is hard-coded in Python.
79
74
80
75
We can make a one-off dynamic vocabulary using a context source binder.
81
-
This is simply a callable (usually a function or an object with a `__call__` method).
76
+
This is a callable, usually a function or an objectwith a `__call__` method.
82
77
It provides the `IContextSourceBinder` interface and takes a `context` parameter.
83
-
The `context` argument is the context of the form
84
-
(i.e. the folder on an add form, and the content object on an edit form).
85
-
The callable should return a vocabulary,
86
-
which is most easily achieved by using the `SimpleVocabulary` class from [zope.schema].
78
+
The `context` argument is the context of the form, in other words, the folder on an add form, and the content object on an edit form.
79
+
The callable should return a vocabulary, which is most easily achieved by using the `SimpleVocabulary`classfrom`zope.schema`.
87
80
88
81
Here is an example using a function to returnall users in a particular group:
We use the PluggableAuthService API to get the group and its members.
111
+
We use the `PluggableAuthService`API to get the group and its members.
119
112
A list of `terms`is created.
120
113
The listis passed to the constructor of a `SimpleVocabulary`.
121
114
The `SimpleVocabulary`objectis returned.
122
115
123
-
When working with vocabularies,
124
-
you’ll come across some terminology that is worth explaining:
125
-
126
-
- A *term* is an entry in the vocabulary.
127
-
The term has a value.
128
-
Most terms are *tokenised* terms which also have a token,
129
-
and some terms are *titled*,
130
-
meaning they have a title that is different to the token.
131
-
- The *token* must be an ASCII string.
132
-
It is the value passed with the request when the form is submitted.
133
-
A token must uniquely identify a term.
134
-
- The *value* is the actual value stored on the object.
135
-
This is not passed to the browser or used in the form.
136
-
The value is often a unicode object, but can be any type of object.
137
-
- The *title* is a unicode object or translatable message (`zope.i18nmessageid`).
138
-
It is used in the form.
116
+
When working with vocabularies, you'll come across some terminology that is worth explaining:
117
+
118
+
- A *term*is an entry in the vocabulary.
119
+
The term has a value.
120
+
Most terms are *tokenized* terms which also have a token, and some terms are *titled*, meaning they have a title that is different to the token.
121
+
- The *token* must be an ASCII string.
122
+
It is the value passed with the request when the form is submitted.
123
+
A token must uniquely identify a term.
124
+
- The *value*is the actual value stored on the object.
125
+
This isnot passed to the browser or used in the form.
126
+
The value is often a Unicode object, but can be anytype of object.
127
+
- The *title*is a Unicode objector translatable message (`zope.i18nmessageid`).
128
+
It is used in the form.
139
129
140
130
The `SimpleVocabulary`class contains two class methods that can be used to create vocabularies from lists:
141
131
142
132
`fromValues()`
143
-
144
-
: takes a simple list of values and returns a tokenised vocabulary where
145
-
the values are the items in the list, and the tokens are created by
146
-
calling `str()` on the values.
133
+
: takes a simple list of values and returns a tokenized vocabulary where the values are the items in the list, and the tokens are created by calling `str()` on the values.
147
134
148
135
`fromItems()`
136
+
: takes a list of `(token, value)` tuples and creates a tokenized vocabulary with the token and value specified.
149
137
150
-
: takes a list of `(token, value)` tuples and creates a tokenised
151
-
vocabulary with the token and value specified.
152
-
153
-
You can also instantiate a `SimpleVocabulary` yourself and pass a list
154
-
of terms in the initialiser.
155
-
The `createTerm()` class method can be used to create a term from a
156
-
`value`, `token` and `title`. Only the value is required.
138
+
You can also instantiate a `SimpleVocabulary` yourself andpass a list of terms in the initializer.
139
+
The `createTerm()`class method can be used to create a term from a `value`, `token`, and`title`.
140
+
Only the value is required.
157
141
158
-
Also to mention, `plone.app.vocabularies` has some helpers creating unicode safe vocabularies.
142
+
Also to mention, `plone.app.vocabularies` has some helpers creating Unicode safe vocabularies.
159
143
160
-
In the example above, we have chosen to create a `SimpleVocabulary` from
161
-
terms with the user id used as value and token, and the user’s full name
162
-
as a title.
144
+
In the example above, we have chosen to create a `SimpleVocabulary`from terms with the user id used as value and token, and the user's full name as a title.
163
145
164
146
To use this context source binder, we use the `source` argument to the `Choice` constructor:
165
147
166
148
```python
167
149
organizer= schema.Choice(
168
-
title=_(u"Organiser"),
150
+
title=_('organizer"),
169
151
source=possibleOrganizers,
170
152
required=False,
171
153
)
172
154
```
173
155
174
-
## Parameterised sources
175
156
176
-
We can improve this example by moving the group name out of the function,
177
-
allowing it to be set on a per-field basis.
178
-
To do so, we turn our `IContextSourceBinder` into a class that is initialised with the group name:
157
+
## parameterized sources
158
+
159
+
We can improve this example by moving the group name out of the function, allowing it to be set on a per-field basis.
160
+
To do so, we turn our `IContextSourceBinder` into a class that is initialized with the group name:
179
161
180
162
```python
181
163
from zope.interface import implementer
@@ -210,42 +192,35 @@ class GroupMembers(object):
210
192
return SimpleVocabulary(terms)
211
193
```
212
194
213
-
Again, the source is set using the `source` argument to the `Choice`
214
-
constructor:
195
+
Again, the source isset using the `source` argument to the `Choice` constructor:
215
196
216
197
```python
217
198
organizer= schema.Choice(
218
-
title=_(u"Organiser"),
199
+
title=_('organizer"),
219
200
source=GroupMembers('organizers'),
220
201
required=False,
221
202
)
222
203
```
223
204
224
-
When the schema is initialised on startup, a `GroupMembers` object
225
-
is instantiated, storing the desired group name. Each time the
226
-
vocabulary is needed, this object will be called (i.e. the
227
-
`__call__()` method is invoked) with the context as an argument,
228
-
expected to return an appropriate vocabulary.
205
+
When the schema is initialized on startup, a `GroupMembers`objectis instantiated, storing the desired group name.
206
+
Each time the vocabulary is needed, this object will be called (in other words, the `__call__()` method is invoked) with the context as an argument, expected to return an appropriate vocabulary.
207
+
229
208
230
209
## Named vocabularies
231
210
232
211
Context source binders are great for simple dynamic vocabularies.
233
-
They are also re-usable, since you can import the source from a single location and use it in multiple instances.
212
+
They are also reusable, since you can import the source from a single location and use it in multiple instances.
234
213
235
214
Sometimes, however, we want to provide an additional level of decoupling, by using *named* vocabularies.
236
-
These are similar to context source binders,
237
-
but are components registered as named utilities,
238
-
referenced in the schema by name only.
239
-
This allows local overrides of the vocabulary via the Component Architecture,
240
-
and makes it easier to distribute vocabularies in third party packages.
215
+
These are similar to context source binders, but are components registered as named utilities, referenced in the schema by name only.
216
+
This allows local overrides of the vocabulary via the Component Architecture, and makes it easier to distribute vocabularies in third party packages.
241
217
242
-
:::{note}
243
-
Named vocabularies cannot be parameterised in the way as we did with the `GroupMembers` context source binder,
244
-
since they are looked up by name only.
245
-
:::
218
+
```{note}
219
+
Named vocabularies cannot be parameterized in the way as we did with the `GroupMembers` context source binder, since they are looked up by name only.
220
+
```
246
221
247
222
We can turn our first "members in the *organizers* group" vocabulary into a named vocabulary by creating a named utility providing `IVocabularyFactory`.
248
-
Create a vocabulary factory in `vocabularies.py`:
223
+
Create a vocabulary factory in{file}`vocabularies.py`:
249
224
250
225
```python
251
226
from zope.schema.interfaces import IVocabularyFactory
We can make use of this vocabulary in any schema by passing its name to
286
-
the `vocabulary` argument of the `Choice` field constructor:
260
+
We can make use of this vocabulary inany schema by passing its name to the `vocabulary` argument of the `Choice` field constructor:
287
261
288
262
```python
289
263
organizer= schema.Choice(
290
-
title=_(u"Organiser"),
291
-
vocabulary=u"example.conference.organizers",
264
+
title=_('organizer"),
265
+
vocabulary='example.conference.organizers",
292
266
required=False,
293
267
)
294
268
```
295
269
270
+
296
271
## Using common vocabularies
297
272
298
-
As you might expect,
299
-
there are a number of standard vocabularies that come with Plone.
300
-
These are found in the [plone.app.vocabularies] package.
301
-
A resent and complete list can be found in the README of the package.
273
+
As you might expect, there are a number of standard vocabularies that come with Plone.
274
+
These are found in the [`plone.app.vocabularies`](https://pypi.org/project/plone.app.vocabularies/) package.
275
+
A recent and complete list can be found in the README of the package.
302
276
303
-
For our example we could use `plone.app.vocabularies.Users`,
304
-
that lists the users of the portal.
277
+
For our example we could use `plone.app.vocabularies.Users`, which lists the users of the portal.
305
278
306
-
The `organizer` field now looks like:
279
+
The `organizer` field should now appear as shown.
307
280
308
281
```python
309
282
organizer= schema.Choice(
310
-
title=_(u"Organiser"),
311
-
vocabulary=u"plone.app.vocabularies.Users",
283
+
title=_('organizer"),
284
+
vocabulary='plone.app.vocabularies.Users",
312
285
required=False,
313
286
)
314
287
```
315
288
289
+
316
290
## The autocomplete selection widget
317
291
318
292
The `organizer` field now has a query-based source.
319
293
The standard selection widget (a drop-down list) isnot capable of rendering such a source.
320
294
Instead, we need to use a more powerful widget.
321
-
For a basic widget, see [z3c.formwidget.query].
322
-
But, in a Plone context, you will more likely want to use [plone.formwidget.autocomplete],
323
-
which extends `z3c.formwidget.query` to provide friendlier user interface.
295
+
For a basic widget, see [`z3c.formwidget.query`](https://pypi.org/project/z3c.formwidget.query/).
296
+
But, in a Plone context, you will more likely want to use [`plone.formwidget.autocomplete`](https://pypi.org/project/plone.formwidget.autocomplete/), which extends `z3c.formwidget.query` to provide a friendlier user interface.
324
297
325
-
The widget is provided with [plone.app.dexterity],
326
-
so we do not need to configure it ourselves.
327
-
We only need to tell Dexterity to use this widget instead of the default,
328
-
using a form widget hint as shown earlier.
329
-
At the top of `program.py`, we add the following import:
298
+
The widget is provided with [`plone.app.dexterity`](https://pypi.org/project/plone.app.dexterity/), so we do not need to configure it ourselves.
299
+
We only need to tell Dexterity to use this widget instead of the default, using a form widget hint as shown earlier.
300
+
At the top of {file}`program.py`, we add the following import:
330
301
331
302
```python
332
303
from plone.formwidget.autocomplete import AutocompleteFieldWidget
333
304
```
334
305
335
-
:::{note}
336
-
If we were using a multi-valued field,
337
-
such as a `List` with a `Choice``value_type`,
338
-
we would use the `AutocompleteMultiFieldWidget` instead.
339
-
:::
306
+
```{note}
307
+
If we were using a multi-valued field, such as a `List`with a `Choice`for`value_type`, we would use the `AutocompleteMultiFieldWidget` instead.
308
+
```
340
309
341
-
In the `IProgram` schema (which, recall, derives from `model.Schema` and is therefore processed for form hints at startup),
310
+
In the `IProgram` schema (whichderives from`model.Schema`,andis therefore processed for form hints at startup),
0 commit comments