How to use validation on ArcheTypes fields to log a specific value change

My situation: Plone 4.3.x, Archetypes custom content type. Items of this custom content type are created when a cron job detects that an external service (SalesForce) has new data. There is an issue with a specific field within this content type. On creation, the field has the right value. But at some later point, certain specific items of this content type get updated and the field ends up with the "wrong" value, which should not happen. (Note: this is not an "illegal" value, it is perfectly valid, but it should not happen for business logic reasons, and the logic is too complicated to express in a validation rule.) I need to figure out why this is happening, whether it's user action, or whether there is some other cause. Logging every change would be prohibitive, so I was wondering how to go about only logging when a particular field undergoes a particular value change, also based on other characteristics of the object that contains this field.
I am not very good with ArcheTypes, so I was wondering if there is a good way to do this:

  • Perhaps create a validator for this particular field (other suggestions welcome).
  • Does the validator have knowledge of the field value before it was changed, as well as the new value? (I'm not sure if the validator gets called when the new field value is still on the request, and the old value is still on the object, or whether it gets called after the new value has already replaced the value in the object.)
  • Does the validator have access to other fields of the object?
  • I don't really want to disallow any change (i.e. "fail" the validation), only log something on certain conditions.

Needless to say, I'm looking for a solution along these lines because I have not been able to manually reproduce the issue, so I need to put something in place on the production system to "catch" it when it happens.

Could you create an event subscriber like https://docs.plone.org/4/en/appendices/five-grok/core-components/events.html or https://docs.plone.org/develop/addons/components/events.html

e.g. https://docs.plone.org/develop/addons/components/events.html#modified-events lists

  • Products.Archetypes.interfaces.IObjectEditedEvent
    called for Archetypes-based objects that are not in the creation stage any more.
    Products.Archetypes.interfaces.IObjectEditedEvent is fired after reindexObject() is called. If you manipulate your content object in a handler for this event, you need to manually reindex new values, or the changes will not be reflected in the portal_catalog.
  • zope.lifecycleevent.IObjectModifiedEvent
    called for creation-stage events as well, unlike the previous event type.

Ah, yes, thanks for the idea. I had not thought of using an event subscriber.

1 Like

Too bad that by the time the event (IObjectModifiedEvent) is triggered, there does not seem to be a way to see what the field values were before the change. The only way around it seems to be to add a second subscriber, for IEditBegunEvent, and save the field I'm interested in in a temporary attribute. Or am I missing something?

Since you are using a custom content type and have full control you still can use a custom mutator:

yourField = atapi.Schema((
    ...
    StringField('foo',
        mutator='notifiedSetFoo',
        ...
    )
))

security.declareProtected('Modify portal content', 'notifiedSetFoo')
class YourContentType(BaseObject):

    schema = yourSchema

    def notifiedSetFoo(self, value, **kwargs):
        old_value = self.getField('foo').get(self)
        if something(old_value, value):
            pass  # your notification
        self.getField('foo').set(self, value)
1 Like

Oooh, I like this, thanks!