Content type schema has no fields based on FTI ._p_mtime
Opened this issue · 0 comments
So, this was a fun bug to debug (NOT)
The model used to define fields for a content type is a generated schema, which uses the FTI ._p_mtime
to generate its unique name. In addition to this, there is a string replacement that replaces .
with _2_
and then joins parts using _0_
.
The _p_mtime
is a timestamp, and when you don't have milliseconds in your datetime object, it will always end in .0
So, this works fine
>>> from plone.dexterity.schema import SchemaNameEncoder
>>> from datetime import datetime
>>> enc = SchemaNameEncoder()
>>> now = datetime.now()
>>> now.timestamp()
1650381570.771721
>>> enc.join("Plone", "1650381570.771721", "Image")
'Plone_0_1650381570_2_771721_0_Image'
>>> enc.split('Plone_0_1650381570_2_771721_0_Image')
['Plone', '1650381570.771721', 'Image']
This doesn't
>>> from plone.dexterity.schema import SchemaNameEncoder
>>> from datetime import datetime
>>> enc = SchemaNameEncoder()
>>> now = datetime(2022,4,19,12,21,30)
>>> now.timestamp()
1650388890.0
>>> enc.join("Plone", "1650388890.0", "Image")
'Plone_0_1650388890_2_0_0_Image'
>>> enc.split('Plone_0_1650388890_2_0_0_Image')
['Plone', '1650388890_2', '0_Image']
I cannot figure out why, or under which circumstances, my Image
FTI ends up with a timestamp from a datetime with no milliseconds
>>> fti = queryUtility(IDexterityFTI, name="Image")
>>> fti._p_mtime
1650326400.0
And the way this manifests, is that suddenly your Images have no image
field
>>> api.content.create(container=site, type="Image", id='my-image')
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/opt/plone/eggs/decorator-5.1.1-py3.8.egg/decorator.py", line 232, in fun
return caller(func, *(extras + args), **kw)
File "/vagrant/src/plone.api/src/plone/api/validation.py", line 75, in wrapped
return function(*args, **kwargs)
File "/opt/plone/eggs/decorator-5.1.1-py3.8.egg/decorator.py", line 232, in fun
return caller(func, *(extras + args), **kw)
File "/vagrant/src/plone.api/src/plone/api/validation.py", line 147, in wrapped
return function(*args, **kwargs)
File "/vagrant/src/plone.api/src/plone/api/content.py", line 71, in create
container.invokeFactory(type, content_id, **kwargs)
File "/opt/plone/eggs/plone.dexterity-3.0.0a2-py3.8.egg/plone/dexterity/content.py", line 830, in invokeFactory
return super(Container, self).invokeFactory(
File "/opt/plone/eggs/Products.CMFCore-2.5.4-py3.8.egg/Products/CMFCore/PortalFolder.py", line 299, in invokeFactory
return ttool.constructContent(type_name, self, id, RESPONSE,
File "/opt/plone/eggs/Products.CMFCore-2.5.4-py3.8.egg/Products/CMFCore/TypesTool.py", line 809, in constructContent
ob = info.constructInstance(container, id, *args, **kw)
File "/opt/plone/eggs/Products.CMFCore-2.5.4-py3.8.egg/Products/CMFCore/TypesTool.py", line 308, in constructInstance
return self._constructInstance(container, id, *args, **kw)
File "/opt/plone/eggs/Products.CMFCore-2.5.4-py3.8.egg/Products/CMFCore/TypesTool.py", line 569, in _constructInstance
notify(ObjectCreatedEvent(obj))
File "/opt/plone/eggs/zope.event-4.5.0-py3.8.egg/zope/event/__init__.py", line 32, in notify
subscriber(event)
File "/opt/plone/eggs/zope.component-5.0.1-py3.8.egg/zope/component/event.py", line 27, in dispatch
component_subscribers(event, None)
File "/opt/plone/eggs/zope.component-5.0.1-py3.8.egg/zope/component/_api.py", line 134, in subscribers
return sitemanager.subscribers(objects, interface)
File "/opt/plone/eggs/zope.interface-5.4.0-py3.8-linux-x86_64.egg/zope/interface/registry.py", line 448, in subscribers
return self.adapters.subscribers(objects, provided)
File "/opt/plone/eggs/zope.interface-5.4.0-py3.8-linux-x86_64.egg/zope/interface/adapter.py", line 899, in subscribers
subscription(*objects)
File "/opt/plone/eggs/zope.component-5.0.1-py3.8.egg/zope/component/event.py", line 36, in objectEventNotify
component_subscribers((event.object, event), None)
File "/opt/plone/eggs/zope.component-5.0.1-py3.8.egg/zope/component/_api.py", line 134, in subscribers
return sitemanager.subscribers(objects, interface)
File "/opt/plone/eggs/zope.interface-5.4.0-py3.8-linux-x86_64.egg/zope/interface/registry.py", line 448, in subscribers
return self.adapters.subscribers(objects, provided)
File "/opt/plone/eggs/zope.interface-5.4.0-py3.8-linux-x86_64.egg/zope/interface/adapter.py", line 899, in subscribers
subscription(*objects)
File "/vagrant/src/plone.app.contenttypes/plone/app/contenttypes/subscribers.py", line 14, in set_title_description
datafield = obj.image
File "/opt/plone/eggs/plone.dexterity-3.0.0a2-py3.8.egg/plone/dexterity/content.py", line 407, in __getattr__
raise AttributeError(name)
AttributeError: image
When I modify the FTI, the problem is solved
>>> setattr(fti, 'dummy', 1)
>>> transaction.commit()
>>> fti._p_mtime
1650382489.4636827
>>> api.content.create(container=site, type="Image", id='my-image')
<Image at /Plone/my-image>
In order to fix this, I believe it should be enough to simply return the int()
part of the timestamp in
plone.dexterity/plone/dexterity/fti.py
Lines 33 to 50 in a4ddc23