The following code shows two custom FieldSerializer adapters which handle the values of Choice and MultiChoice (Collection) fields. For doing this, we register one adapter for the IChoice interface and one adapter for ICollection interface. Both also support vocabularies provided by collective.taxonomy.
By default the Plone REST API would give us just the plain tokens back, because that's what is actually stored when you use a Selection or Multiselection field. But on the client side we want the token and also the title, so that we can easily show it. Therefor we want something like this as the value for a Choice field.
Expected response
IChoice field
"solution_category": {
"title": "A nice title",
"token": "a-nice-title"
},
ICollection field
"solution_category": [
{
"title": "A nice title",
"token": "a-nice-title"
},
{
"title": "An awesome title",
"token": "an-awesome-title"
},
]
Implementation
restapi.py
from plone.dexterity.interfaces import IDexterityContent
from plone.restapi.interfaces import IFieldSerializer
from plone.restapi.serializer.converters import json_compatible
from zope.component import adapter
from zope.component import getUtility
from zope.interface import implementer
from zope.interface import Interface
from zope.schema.interfaces import IChoice
from zope.schema.interfaces import ICollection
from zope.schema.interfaces import IVocabularyFactory
def _get_vocab_term(context, field, value):
""" Get vocab term dict
returns: {'token': token, 'title': title}
"""
vocab_term = {
'token': None,
'title': None,
}
vocab_term['token'] = value
factory = getUtility(IVocabularyFactory, field)
if not factory:
return vocab_term
# collective.taxonomy support:
if hasattr(factory, 'translate'):
vocab_term['title'] = _get_taxonomy_vocab_title(
context,
factory,
value,
)
elif IVocabularyFactory.providedBy(factory):
vocab_term['title'] = _get_vocab_title(
context,
factory,
value,
)
return vocab_term
def _get_taxonomy_vocab_title(context, factory, value):
vocab_title = factory.translate(
value,
context=context,
)
return vocab_title
def _get_vocab_title(context, factory, value):
vocab = factory(context)
vocab_title = vocab.getTermByToken(value).title
return vocab_title
class BaseFieldSerializer(object):
def __init__(self, field, context, request):
self.context = context
self.request = request
self.field = field
def __call__(self):
return json_compatible(self.get_value())
@adapter(IChoice, IDexterityContent, Interface)
@implementer(IFieldSerializer)
class ChoiceFieldSerializer(BaseFieldSerializer):
"""
"""
def get_value(self, default=None):
value = getattr(
self.field.interface(self.context),
self.field.__name__,
default,
)
if not value:
return
term = _get_vocab_term(
self.context,
self.field.vocabularyName,
value,
)
return term
@adapter(ICollection, IDexterityContent, Interface)
@implementer(IFieldSerializer)
class CollectionFieldSerializer(BaseFieldSerializer):
"""
"""
def get_value(self, default=None):
terms = []
values = getattr(
self.field.interface(self.context),
self.field.__name__,
default,
)
if not values:
return
if not IChoice.providedBy(self.field.value_type):
return
for value in values:
term = _get_vocab_term(
self.context,
self.field.value_type.vocabularyName,
value,
)
terms.append(term)
return terms
Registration
To activate this, we need to register the two adapters:
configure.zcml
<adapter factory=".restapi.ChoiceFieldSerializer" />
<adapter factory=".restapi.CollectionFieldSerializer" />