Dynamic Vocabularies in Plone Archetypes

There are certain instances where you will need a vocabulary to be dynamic in your Archetype instance. One example is if you want to display a list of objects contained in a Plone folder in a SelectionWidget.

The first thing to understand is that many archetype field attributes allow you to refer to a function, instead of a string.  Until now, you’ve probably been using vocabularies with a DisplayList that you’ve defined in your config.py file:

HOUR = DisplayList((
('1', '1'),
('2', '2'),
etc....
))
StringField('hour',
vocabulary=HOUR,
widget=SelectionWidget(label='Hour',),
required=1,
),
We could just have easily defined a function to generate the HOUR DisplayList and place it in our class definition, using the DisplayList add method to build a DisplayList on the fly.

class Event(ATContent):
def getHours(self):
dl = DisplayList()
for x in range(1, 13):
dl.add(str(x))
dl.add(str(x))
return dl

Now, we can call getHours from our archetypes field definition:

StringField('hour',
vocabulary="getHours",
widget=SelectionWidget(label='Hour',),
required=1,
),

Now your thinking, there’s got to be something more useful than creating a vocabulary of numbers from 1 to 12.  What if I need to show a list of sibling objects in the same Plone folder?   Let’s now combine what we’ve just learned with Acquisition.  First include this import at the top of your script:

from Acquisition import aq_parent
Now, in the same way that we created getHours above, create a getLinks method to return all Link objects in the current folder:

def getLinks(self):
dl = DisplayList()
linklist = aq_parent(self).contentValues(filter={'portal_type' : 'Link'})
for link in linklist:
dl.add(link['id'], link['title'])

return dl

Then call getLinks from the vocabulary attribute:

StringField('link',
vocabulary="getLinks",
widget=SelectionWidget(label='Choose an existing link',),
required=1,
),