Zope.Schema/Plone - How can I set the value of a Datetime field in an updateWidget function?

640 views Asked by At

On a plone site I am working on, I have a form that is used to edit records of objects stored mapped to a table in the backend database. In the interface class, one of the fields is a schema.Datetime field.

class ICalibration(Interface):
"""Interface class for calibration records
"""
...
    Last_Calibration = schema.Datetime(title=u"Last Calibration"
                                       description=u"date of last calibration",


                                      )
...

In my updateWidgets function, in the edit form, I try to set the value of the widget,

class EditCalibration(form.Form):
    grok.name('edit-calibration')
    grok.require('zope2.View')
    grok.context(ISiteRoot)

    def updateWidgets(self):
        super(EditCalibration, self).updateWidgets()
        id = self.request.get('id',None)

        if id:
            currentCal = session.query(Calibration).filter(Calibration.Calibration_ID == id).one()

            ...
            self.widgets["Last_Calibration"].value = currentCal.Last_Calibration
        ...

but I get this error:

"TypeError: 'datetime.datetime' object has no attribute 'getitem'.

I did try some things that were sort of interesting. I printed the value of cal.Last_Calibration and its coming out as the date I put in when I added the record.
I tried printing the type of object that cal.Last_Calibration was and it was python's datetime (as opposed to zope's I believe?). I tried setting the field equal to today's date: datetime.today() and got the same error. Just for kicks, I also tried converting currentCal.Last_Calibration to a string and passing it into the field, although that just put random numbers inside the fields.

For the record, I imported python's datetime as:

from datetime import datetime

Also, adding a record/calibration works fine, so its not an issue with the database or the sqlalchemy schema I am using.

If it's possible, what is the appropriate way of setting the schema field's value in a updateWidgets function?

Should I use a different widget? If so, all I really need for my form is the date. The add/update function will take a datetime object, so I could create a datetime object from the data, regardless of the type I believe.

1

There are 1 answers

3
David Glick On BEST ANSWER

In the z3c.form framework the following steps happen to get the widget value:

  1. If the request already has a value (from a previous form submission), it is used.
  2. If form.ignoreContext == False (the default), it calls form.getContent() to get the "content object" that the form is editing (which defaults to form.context).
  3. If the content object provides the schema interface the field came from, the "field value" will be fetched as an attribute of the object. (If it does not provide the schema interface, z3c.form looks for an adapter of the object to the schema interface, and gets the attribute on the adapter. If the content object is a dict then the field value will be fetched as an item of the dict rather than using attribute lookup.)
  4. A "converter" based on the field type and widget type is used to convert the field value into a widget value. The difference is that the field value is the actual stored value, while the widget value is a string serialization of that value.

Your problem is that you are trying to set the widget value to a datetime rather than the serialization of that datetime that is expected by the widget.

I would do what you're trying to do by overriding getContent instead of updateWidgets:

from five import grok
from plone.directives import form

class EditCalibration(form.SchemaForm):
    grok.name('edit-calibration')
    grok.require('zope2.View')
    grok.context(ISiteRoot)

    schema = ICalibration

    def getContent(self):
        id = self.request.get('id', None)
        if id:
            return session.query(Calibration).filter(Calibration.Calibration_ID == id).one()

You will also need to declare that your Calibration class implements the ICalibration interface, so that z3c.form recognizes that it can get Last_Calibration as an attribute of the Calibration instance. That should look something like this:

from zope.interface import implementer

@implementer(ICalibration)
class Calibration(Base):
    etc...