plone / plone.restapi

RESTful API for Plone.
http://plonerestapi.readthedocs.org/
90 stars 79 forks source link

How to write a deseriaizer for z3cform.datagridfield/? Weakness of the deserialization adapter approach. #510

Open zopyx opened 6 years ago

zopyx commented 6 years ago

Related discussion:

https://community.plone.org/t/plone-restapi-serialization-deserialization-of-collective-z3cform-datagridfield-fields/5984/4

I have a z3cform.datagridfield with the following field

    form.widget(table=DataGridFieldFactory)
    table = schema.List(title=_(u'Seminar dates'),
                        required=False,
                        value_type=DictRow(title=_(u'Seminar date'), schema=ITableRowSchema))

and a related row definition

class ITableRowSchema(form.Schema):

    date_from = schema.Date(
        title=_(u'Start date'),
        required=True,
        defaultFactory=default_date
    )

It is obvious that we can not send python date instances as JSON over Plone REST api.

The first draft of a deserializer looks like this

@implementer(IFieldDeserializer)
@adapter(IList, IDexterityContent, IBrowserRequest)
class GridDeserializer(DefaultFieldDeserializer):

    def __call__(self, value):

        print value
        return [{u'duration': u'3', u'languages': [u'de', u'en'], u'date_from': datetime.date.today()}]

        # Mimic what z3c.form does in it's BaseDataConverter.
        if isinstance(value, unicode):
            value = value.strip()
            if value == u'':
                value = self.field.missing_value

        self.field.validate(value)
        return value

Note that this serializer is for IList and very generic and called for basically all IList fields in dependent of the content type. The serializer also has access to the value and no other context information in order to proceed with a more content-type specific deserialization.

zopyx commented 6 years ago

Best approach/workaround so far is this:

from zope.interface import implementer
import datetime
import dateutil.parser
from zope.component import adapter

from plone.restapi.interfaces import IFieldDeserializer
from plone.dexterity.interfaces import IDexterityContent
from zope.publisher.interfaces.browser import IBrowserRequest
from plone.restapi.deserializer.dxfields import DefaultFieldDeserializer
from collective.z3cform.datagridfield.interfaces import IDataGridField
from zope.schema.interfaces import IList

@implementer(IFieldDeserializer)
@adapter(IList, IDexterityContent, IBrowserRequest)
class GridDeserializer(DefaultFieldDeserializer):

    def __call__(self, value):
        result = list()
        for d in value:
            d = d.copy()
            if 'date_from' in d:
                d['date_from'] = dateutil.parser.parse(d['date_from']).date()
            result.append(d)
        return result
tisto commented 6 years ago

This is indeed a missing piece. Might be related: https://github.com/plone/plone.restapi/pull/177

I would love to see this in plone.restapi core.