Zope.Schema/Plone - How can I set the value of a Datetime field in an updateWidget function?
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.
Answer
The issue you're encountering is likely because the Last_Calibration
field in your form is expecting a zope.schema.datatypes.DateTime
object, but you are passing a native Python datetime.datetime
object from SQLAlchemy. This mismatch causes the error you're seeing, where Python tries to access the datetime.datetime
object using dictionary-style indexing (getitem
), which is not possible.
To fix this issue, you need to ensure that you're passing the correct type to the form widget, which expects a datetime.datetime
object but wrapped in a zope.schema.datatypes.DateTime
format if necessary.
Here’s how to address this:
Step 1: Ensure that currentCal.Last_Calibration
is a datetime
object
First, confirm that currentCal.Last_Calibration
is indeed a datetime.datetime
object and not something else like a string. You can print the type to verify this:
print(type(currentCal.Last_Calibration))
If it prints something like <class 'datetime.datetime'>
, you're good to go. If it's something like a string, you will need to convert it into a datetime.datetime
object first, for example:
from datetime import datetime
# If it's a string in the form 'YYYY-MM-DD HH:MM:SS'
currentCal.Last_Calibration = datetime.strptime(currentCal.Last_Calibration, "%Y-%m-%d %H:%M:%S")
Step 2: Set the Last_Calibration
widget value
Now that you’ve confirmed that currentCal.Last_Calibration
is a datetime
object, the next step is to properly set the value for the Last_Calibration
widget. The updateWidgets
function allows you to set values for the widgets programmatically.
The following should work as expected:
from datetime import datetime
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:
# Fetch the record using the ID
currentCal = session.query(Calibration).filter(Calibration.Calibration_ID == id).one()
# Assuming currentCal.Last_Calibration is a datetime object:
self.widgets["Last_Calibration"].value = currentCal.Last_Calibration
Here, we directly assign the datetime
object (currentCal.Last_Calibration
) to the widget’s value.
Step 3: Handle conversion between different formats
In case you are working with forms and widgets where the date might be in a different format (for example, string format), you can convert it like so:
from datetime import datetime
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:
# Fetch the record using the ID
currentCal = session.query(Calibration).filter(Calibration.Calibration_ID == id).one()
# If the currentCal.Last_Calibration is a string, convert it to a datetime object
if isinstance(currentCal.Last_Calibration, str):
currentCal.Last_Calibration = datetime.strptime(currentCal.Last_Calibration, "%Y-%m-%d %H:%M:%S")
# Now set the widget's value to the datetime object
self.widgets["Last_Calibration"].value = currentCal.Last_Calibration
In this case, if the Last_Calibration
field is a string, it will be converted to a datetime.datetime
object using datetime.strptime()
.
Step 4: Handle empty fields
If there is a possibility that currentCal.Last_Calibration
could be None
(i.e., no value set), you can add a check for this and set the widget value to None
or leave it empty:
if currentCal.Last_Calibration:
self.widgets["Last_Calibration"].value = currentCal.Last_Calibration
else:
self.widgets["Last_Calibration"].value = None
Conclusion
In summary, ensure that you are passing a valid datetime.datetime
object to the Last_Calibration
widget. If necessary, convert any string representations of the datetime to the appropriate datetime
object before setting it to the widget. This will resolve the issue you're seeing with the getitem
error.