Skip to content

Commit 84677d8

Browse files
committed
Tidy vocabularies.md
1 parent e366c16 commit 84677d8

File tree

1 file changed

+90
-130
lines changed

1 file changed

+90
-130
lines changed
Lines changed: 90 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,82 @@
11
---
22
myst:
33
html_meta:
4-
"description": ""
5-
"property=og:description": ""
6-
"property=og:title": ""
4+
"description": "Vocabularies"
5+
"property=og:description": "Vocabularies"
6+
"property=og:title": "Vocabularies"
77
"keywords": ""
88
---
99

1010
# Vocabularies
1111

12-
**Creating your own static and dynamic vocabularies**
12+
This chapter describes how to create your own static and dynamic vocabularies.
1313

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/).
1715

1816
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:
2118

2219
```python
2320
class IMySchema(model.Schema):
2421
myChoice = schema.Choice(...)
2522
```
2623

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`:
2825

2926
```python
3027
class IMySchema(model.Schema):
3128

3229
myList = schema.List(
33-
...,
3430
value_type=schema.Choice(...)
3531
)
3632
```
3733

38-
The choice field must be passed one of the following arguments:
34+
The `Choice` field must be passed one of the following arguments:
3935

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.
4341

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.
4642

4743
## Static vocabularies
4844

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:
5348

5449
```python
5550
from zope.schema.vocabulary import SimpleTerm
5651
from zope.schema.vocabulary import SimpleVocabulary
5752

5853
organizers = SimpleVocabulary(
5954
[
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'))
6358
]
6459
)
6560

6661
organizer = schema.Choice(
67-
title=_(u"Organiser"),
62+
title=_('organizer"),
6863
vocabulary=organizers,
6964
required=False,
7065
)
7166
```
7267

7368
Since `required` is `False`, there will be a {guilabel}`no value` option in the drop-down list.
7469

70+
7571
## Dynamic sources
7672

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.
7974

8075
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 object with a `__call__` method.
8277
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` class from `zope.schema`.
8780

8881
Here is an example using a function to return all users in a particular group:
8982

@@ -115,67 +108,56 @@ def possibleOrganizers(context):
115108
return SimpleVocabulary(terms)
116109
```
117110

118-
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.
119112
A list of `terms` is created.
120113
The list is passed to the constructor of a `SimpleVocabulary`.
121114
The `SimpleVocabulary` object is returned.
122115

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 is not passed to the browser or used in the form.
126+
The value is often a Unicode object, but can be any type of object.
127+
- The *title* is a Unicode object or translatable message (`zope.i18nmessageid`).
128+
It is used in the form.
139129

140130
The `SimpleVocabulary` class contains two class methods that can be used to create vocabularies from lists:
141131

142132
`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.
147134

148135
`fromItems()`
136+
: takes a list of `(token, value)` tuples and creates a tokenized vocabulary with the token and value specified.
149137

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 and pass 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.
157141

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.
159143

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.
163145

164146
To use this context source binder, we use the `source` argument to the `Choice` constructor:
165147

166148
```python
167149
organizer = schema.Choice(
168-
title=_(u"Organiser"),
150+
title=_('organizer"),
169151
source=possibleOrganizers,
170152
required=False,
171153
)
172154
```
173155

174-
## Parameterised sources
175156

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:
179161

180162
```python
181163
from zope.interface import implementer
@@ -210,42 +192,35 @@ class GroupMembers(object):
210192
return SimpleVocabulary(terms)
211193
```
212194

213-
Again, the source is set using the `source` argument to the `Choice`
214-
constructor:
195+
Again, the source is set using the `source` argument to the `Choice` constructor:
215196

216197
```python
217198
organizer = schema.Choice(
218-
title=_(u"Organiser"),
199+
title=_('organizer"),
219200
source=GroupMembers('organizers'),
220201
required=False,
221202
)
222203
```
223204

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` object is 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+
229208

230209
## Named vocabularies
231210

232211
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.
234213

235214
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.
241217

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+
```
246221

247222
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`:
249224

250225
```python
251226
from zope.schema.interfaces import IVocabularyFactory
@@ -272,95 +247,80 @@ def organizers_vocabulary_factory(context):
272247
return SimpleVocabulary(terms)
273248
```
274249

275-
The add to your `configure.zcml`.
250+
Then add to your {file}`configure.zcml`.
276251
By convention, the vocabulary name is prefixed with the package name, to ensure uniqueness.
277252

278253
```xml
279254
<utility
280-
name="example.conference.organisers"
255+
name="example.conference.organizers"
281256
component="example.conference.vocabularies.organizers_vocabulary_factory"
282257
/>
283258
```
284259

285-
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 in any schema by passing its name to the `vocabulary` argument of the `Choice` field constructor:
287261

288262
```python
289263
organizer = schema.Choice(
290-
title=_(u"Organiser"),
291-
vocabulary=u"example.conference.organizers",
264+
title=_('organizer"),
265+
vocabulary='example.conference.organizers",
292266
required=False,
293267
)
294268
```
295269

270+
296271
## Using common vocabularies
297272

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.
302276

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.
305278

306-
The `organizer` field now looks like:
279+
The `organizer` field should now appear as shown.
307280

308281
```python
309282
organizer = schema.Choice(
310-
title=_(u"Organiser"),
311-
vocabulary=u"plone.app.vocabularies.Users",
283+
title=_('organizer"),
284+
vocabulary='plone.app.vocabularies.Users",
312285
required=False,
313286
)
314287
```
315288

289+
316290
## The autocomplete selection widget
317291

318292
The `organizer` field now has a query-based source.
319293
The standard selection widget (a drop-down list) is not capable of rendering such a source.
320294
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.
324297

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:
330301

331302
```python
332303
from plone.formwidget.autocomplete import AutocompleteFieldWidget
333304
```
334305

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+
```
340309

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 (which derives from `model.Schema`, and is therefore processed for form hints at startup),
342311
we then add the following:
343312

344313
```python
345314
from plone.autoform import directives
346315

347316
directives.widget(organizer=AutocompleteFieldWidget)
348317
organizer = schema.Choice(
349-
title=_(u'Organiser'),
350-
vocabulary=u'plone.app.vocabularies.Users',
318+
title=_('organizer'),
319+
vocabulary='plone.app.vocabularies.Users',
351320
required=False,
352321
)
353322
```
354323

355-
You should now see a dynamic auto-complete widget on the form,
356-
so long as you have JavaScript enabled.
324+
You should now see a dynamic auto-complete widget on the form, so long as you have JavaScript enabled.
357325
Start typing a user name and see what happens.
358-
The widget also has fall-back for non-JavaScript capable browsers.
359-
360-
[plone.app.dexterity]: http://pypi.python.org/pypi/plone.app.dexterity
361-
[plone.app.vocabularies]: http://pypi.python.org/pypi/plone.app.vocabularies
362-
[plone.formwidget.autocomplete]: http://pypi.python.org/pypi/plone.formwidget.autocomplete
363-
[plone.principalsource]: http://pypi.python.org/pypi/plone.principalsource
364-
[z3c.form]: http://pypi.python.org/pypi/z3c.form
365-
[z3c.formwidget.query]: http://pypi.python.org/pypi/z3c.formwidget.query
366-
[zope.schema]: http://pypi.python.org/pypi/zope.schema
326+
The widget also has a fall-back for non-JavaScript capable browsers.

0 commit comments

Comments
 (0)