Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting field defaults on programmatically created Dexterity items

I have a Dexterity content type based on plone.directives.form.Schema which has a number of form hints for assigning defaults:

@form.default_value(field=ITrial['start_on'])
def default_start_on(data):
    return datetime.now()

Some of the defaults are more complex, passing back objects that are themselves instances of Dexterity types. These objects are essential for the main type's setup, which is triggered by various events.

I'm now in the process of testing. Ideally, I'd like to be able to use something like:

item = createContentInContainer(folder, 'ctcc.model.trial', 'item')

That is, I'd like the defaults to be picked up by the item without having to be manually passed into the constructor.

If I was using zope.schema I could use FieldProperty to set up proxies to the schema fields. Is there something equivalent for Dexterity, or perhaps a function for pushing an object through form creation?

Solution: I ended up going with David's option #1, intercepting ObjectCreatedEvent.

@grok.subscribe(ITrial, IObjectCreatedEvent)
def create_trial(trial, event):            
    if getattr(trial, 'start_on', None) is None:
        trial.start_on = default_start_on(None)

It stills feels like I'm replicating part of form behaviour, but at least it's using the same functions that are providing the form defaults.

like image 253
Matthew Trevor Avatar asked Nov 14 '22 01:11

Matthew Trevor


1 Answers

As you've discovered, the @form.default_value decorator is respected by z3c.form forms, but not when items are created in other ways. You have several options:

  1. Write a handler for the ObjectCreatedEvent for your content type which sets up the necessary default values. This is the simplest approach to implement, but may not work if there are other handlers of this event that need the values in place.

  2. Create your own subclass of Dexterity's generic Item class, and use it instead of Item as the basis for your content type. Then you can customize the __init__ method to set whatever you want. This would require migration of existing content items though, if you already have some.

  3. This is a more complicated option. Replace the factory utility used to construct the content type. createContentInContainer ends up looking for an IFactory utility with a name equal to the factory attribute of the content type's FTI. The default implementation is in plone.dexterity.factory but you could replace it with a different one that does more.

like image 56
David Glick Avatar answered Dec 26 '22 12:12

David Glick