Using IDexterityFTI or iterSchemata() to find the vocabulary for each field

In the model file for my custom portal type I have two fields with vocabularies:

<field name="product_range" type="zope.schema.Choice" >
    <title>Product Range</title>
    <description />
    <vocabulary>my_theme.ProductRangeVocabulary</vocabulary>
</field>

<field name="application_areas" type="zope.schema.Tuple"
       form:widget="plone.app.z3cform.widget.AjaxSelectFieldWidget">
    <title>Application Areas</title>
    <description />
    <value_type type="zope.schema.Choice">
        <vocabulary>my_theme.ApplicationAreasVocabulary</vocabulary>
        <orderable>True</orderable>
    </value_type>
</field>

After reading https://docs.plone.org/external/plone.app.dexterity/docs/reference/manipulating-content-objects.html and https://docs.plone.org/develop/plone/forms/schemas.html

I naively assumed that either of the following approaches would let me find the fields' vocabularies:

schema = getUtility(IDexterityFTI, name='my_portal_type').lookupSchema()
fields = getFieldsInOrder(schema)
for field in fields:
    vocabulary = getattr(field[1], 'vocabulary', None)

or alternatively

schema = iterSchemata(context)
fieldnames = getFields(schema)
for fieldname in fieldnames:
    vocabulary = getattr(schema[fieldname], 'vocabulary', None)

Neither of these work, can someone point me in the right direction?

Coincidentally, I came across http://mrtango.planetcrazy.de/writing-a-custom-serializer-for-plone-rest-api.html @MrTango - but this might be overkill in my case...

what is it what you actually trying to do?
You want the value from the vocabulary or like me having the names/titles for the token?
For the latter you can get the vocabulary with getUtility and then use vocab.getTermByToken(token).title to get the title for a token.

Hi @MrTango - I want to be able to get the vocabulary which is associated with any particular field.

This actually halfway works for me now, and gets me the dotted name of the vocabulary for Tuple and Choice fields

for schemata in iterSchemata(this_obj):
    fields = getFields(schemata)
    for fieldname in fields:
        field = schemata[fieldname]
        value_type = getattr(field, 'value_type', None)
        if value_type:
            vocabulary = getattr(field.value_type, 'vocabularyName', None)
        else:
            vocabulary = getattr(field, 'vocabularyName', None)

		/more code snipped/

        tmb_obj[fieldname] = {'label': label, 'value': value, 'vocabulary': vocabulary }

Then later, I can get the titles for my simpleterms...

if 'vocabulary' in tmb_field.keys() and tmb_field['vocabulary']:
    value_titles = []
    vocabulary_name = tmb_field['vocabulary']
    factory = getUtility(IVocabularyFactory, vocabulary_name)
    vocabulary = factory(context)

    if isinstance(field_value, basestring):
        value_titles.append(translate(vocabulary.getTerm(field_value).title
                        , context = self.context
                        , domain = 'mdb_theme'
                        , target_language = target_language))
    else:
        for value in field_value:
            value_titles.append(translate(vocabulary.getTerm(value).title
                        , context = self.context
                        , domain = 'mdb_theme'
                        , target_language = target_language))

    field_value = ', '.join(value_titles)

Typically, you know the field name. You use this name to access the corresponding field in a schema (schema[field_name)) and then access its vocabulary.

For many fields, the value of the vocabulary attribute is not necessarily a vocabulary itself. Especially, it can be a string. In this case, this string is interpreted as the name of a utility which can be applied to an object and returns a vocabulary. This lookup happens only during the bind call of the field, which binds the field to an object. Before the bind the field does not yet have a vocabulary. For details look at the source, e.g. in zope.schema._bootstrapfields.Choice.