Vocabularies¶
Description
Vocabularies are lists of (value -> human readable title) pairs used by e.g. selection drop downs.
zope.schema
provides tools to programmatically construct their vocabularies.
Introduction¶
Vocabularies specify options for choice fields.
Vocabularies are normally described using zope.schema.vocabulary.SimpleVocabulary
and zope.schema.vocabulary.SimpleTerm
objects.
See the source code.
Vocabulary terms¶
zope.schema
defines different vocabulary term possibilities.
A term is an entry in the vocabulary. The term has a value. Most terms are tokenised terms which also have a token. Some terms are titled, meaning they have a title that is different to the token.
SimpleTerm
instances
SimpleTerm.token
must be an ASCII string. It is the value passed with the request when the form is submitted. A token must uniquely identify a term.
SimpleTerm.value
is the actual value stored on the object. This is not passed to the browser or used in the form. The value is often a unicode string, but can be any type of object.
SimpleTerm.title
is a unicode string or translatable message. It is used for display in the form.
For further details please read the interfaces specification
Note
If you need international texts please note that only title is, and should be, translated. Value and token must always carry the same value.
Creating a vocabulary¶
Example 1 - form a list of tuples:
from zope.schema.vocabulary import SimpleTerm
from zope.schema.vocabulary import SimpleVocabulary
items = [ ('value1', u'This is label for item'), ('value2', u'This is label for value 2')]
terms = [ SimpleTerm(value=pair[0], token=pair[0], title=pair[1]) for pair in items ]
myVocabulary = SimpleVocabulary(terms)
Example 2 - using the vocabulary from (1):
from plone.supermodel import form
from zope import schema
class ISampleSchema(form.Schema):
contentMedias = schema.Choice(
title=u'Test choice'
vocabulary=myVocabulary,
)
Example 3 - create a vocabulary from a list of values:
values = ['foo', 'bar', 'baz']
other_vocabulary = SimpleVocabulary.fromValues(values)
Example 4 - registering as a reusable component, a named utility.
First a vocabulary factory needs to be created.
from zope.schema.interfaces import IVocabularyFactory
from zope.interface import provider
@provider(IVocabularyFactory)
def myvocabulary_factory(context):
return myvocabulary
It has to be registered in ZCML as a utility:
<utility
component=".vocab.myvocabulary_factory"
name="example.myvocabulary"
/>
Retrieving a vocabulary¶
zope.schema
’s SimpleVocabulary objects are retrieved via factories registered as utilities.
To get one, use zope.component’s getUtility:
from zope.component import getUtility
from zope.schema.interfaces import IVocabularyFactory
factory = getUtility(IVocabularyFactory, 'example.myvocabulary')
vocabulary = factory()
Getting a term¶
By term value
term = vocabulary.getTerm('value1')
value, token, term = (term.value, term.token, term.title)
Listing a vocabulary¶
Example - Iterate vocabulary SimpleTerm objects:
for term in vocabulary:
print(term.value, term.token, term.title)
Dynamic vocabularies¶
Dynamic vocabularies’ values may change run-time. They are usually generated based on some context data.
Complete example with portal_catalog query, vocabulary creation and form
"""
A vocabulary example where vocabulary gets populated from portal_catalog query
and then this vocabulary is used in Dexterity form.
"""
from zope.component import provider
from plone.directives import form
from zope import schema
from z3c.form import button
from Products.CMFCore.interfaces import ISiteRoot, IFolderish
from Products.statusmessages.interfaces import IStatusMessage
from zope.schema.interfaces import IContextSourceBinder
from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
def make_terms(items):
""" Create zope.schema terms for vocab from tuples """
terms = [ SimpleTerm(value=pair[0], token=pair[0], title=pair[1]) for pair in items ]
return terms
@provider(IContextSourceBinder)
def course_source(context):
"""
Populate vocabulary with values from portal_catalog.
@param context: z3c.form.Form context object (in our case site root)
@return: SimpleVocabulary containing all areas as terms.
"""
# Get site root from any content item using portal_url tool thru acquisition
root = context.portal_url.getPortalObject()
# Acquire portal catalog
portal_catalog = root.portal_catalog
# We need to get Plone site path relative to ZODB root
# See traversing docs for more info about getPhysicalPath()
site_physical_path = '/'.join(root.getPhysicalPath())
# Target path we are querying
folder_name = "courses"
# Query all folder like objects in the target path
# These portal_catalog query conditions are AND
# but inside keyword query they are OR (the different content types
# we are looking for)
brains = portal_catalog.searchResults(path={ "query": site_physical_path + "/" + folder_name },
portal_type=["CourseInfo", "Folder"] )
# Create a list of tuples (UID, Title) of results
result = [ (brain["UID"], brain["Title"]) for brain in brains ]
# Convert tuples to SimpleTerm objects
terms = make_terms(result)
return SimpleVocabulary(terms)
class IMyForm(form.Schema):
""" Define form fields """
name = schema.TextLine(
title=u"Your name",
)
class MyForm(form.SchemaForm):
""" Define Form handling
This form can be accessed as http://yoursite/@@my-form
"""
grok.name('my-form')
grok.require('zope2.View')
grok.context(ISiteRoot)
schema = IMyForm
ignoreContext = True
@button.buttonAndHandler(u'Ok')
courses = schema.List(title=u"Promoted courses",
required=False,
value_type=schema.Choice(source=course_source)
)
def handleApply(self, action):
data, errors = self.extractData()
if errors:
self.status = self.formErrorsMessage
return
# Do something with valid data here
# Set status on this form page
# (this status message is not bind to the session and does not go through redirects)
self.status = "Thank you very much!"
@button.buttonAndHandler(u"Cancel")
def handleCancel(self, action):
"""User cancelled. Redirect back to the front page.
"""
Complex example 2
from zope.component import provider
from zope.schema.interfaces import IContextSourceBinder
from zope.schema.interfaces import implementer
from zope.schema.vocabulary import SimpleVocabulary, SimpleTerm
from Products.CMFCore.utils import getToolByName
from plone.i18n.normalizer import idnormalizer
def make_terms(items):
""" Create zope.schema terms for vocab from tuples """
terms = [ SimpleTerm(value=pair[0], token=pair[0], title=pair[1]) for pair in items ]
return terms
@provider(IContextSourceBinder)
def area_source(context):
"""
Populate vocabulary with values from portal_catalog.
Custom index name getArea contains utf-8 strings of
possible area field values found on all content objects.
@param context: Form context object.
@return: SimpleVocabulary containing all areas as terms.
"""
# Get catalog brain objects of all accommodation content
accommodations = context.queryAllAccommodation()
# Extract getArea index from the brains
areas = [ a["getArea"] for a in accommodations ]
# result will contain tuples (term, title) of acceptable items
result = []
# Create a form choice "do not filter"
# which is always present
result.append( ("all", _(u"All")) )
# done list filter outs duplicates
done = []
for area in areas:
if area != None and area not in done:
# Archetype accessors return utf-8
area_unicode = area.decode("utf-8")
# Id must be 7-bit
id = idnormalizer.normalize(area_unicode)
# Decode area name to unicode
# show that form shows international area
# names correctly
entry = (id, area_unicode)
result.append(entry)
done.append(area)
# Convert tuples to SimpleTerm objects
terms = make_terms(result)
return SimpleVocabulary(terms)
For another example, see the Dynamic sources chapter in the Dexterity manual.
Registering a named vocabulary provider in ZCML¶
You can use <utility>
in ZCML to register vocabularies by name
and then refer them by name via getUtility()
or in zope.schema.Choice.
<utility
provides="zope.schema.interfaces.IVocabularyFactory"
component="zope.app.gary.paths.Favorites"
name="garys-favorite-path-references"
/>
Then you can refer to vocabulary by its name:
class ISearchCriteria(form.Schema):
""" Alternative header flash animation/imagae """
area = schema.Choice(source="garys-favorite-path-references", title=_("Area"), required=False)
For more information see: