Override a schema provided by a behavior

What is the recommended way to override/alter a registered behavior?

In the document on creating and registering behaviors
The example shows collective.gtags and includes a schema like the one below. It doesn't discuss how a second product might be used to override the registered behavior.

@provider(IFormFieldProvider)
class ITags(model.Schema):
    """Add tags to content
    """

    directives.fieldset(
            'categorization',
            label=_(u'Categorization'),
            fields=('tags',),
        )

    tags = Tags(
            title=_(u"Tags"),
            description=_(u"Applicable tags"),
            required=False,
            allow_uncommon=True,
        )

What if I wanted to change the behavior, for example, move the tags to the default field set or make tags required.

Update.
This was discussed, at least 5 years ago, already, but it doesn't speak conclusively about the final approach to the problem How to change existing dexterity types and behaviors

Coming to a final standard approach was raised again more recently here --> Patterns and anti-patterns with behaviors?

Some remarks:

  • breaking a behavior with a compound of multiple fields into a different behavior with less fields (e.g. when you want to get rid of a field) is not easy.. I tend to create a a new behavior (with code duplication)

  • it is possible to monkey patch the definition of a field e.g. I injected a validator for the description field using

    IBasic['description'].__dict__['constraint'] = my_validator_function

I override the behavior (wildcard.media) with object inheritance. Seems the cleanest and easiest to me so far.

Basically, create a new behavior and stick it full of stuff from the old behavior. Then change only what you need.

'MyProduct' is called 'rfasite' - I'll just cut and paste.

rfasite/content/behaviors/configure.zcml

   <plone:behavior
      title="RFA Audio"
      description="Add support for audio fields"
      provides="rfasite.content.behaviors.audio.IRFAAudio"
      factory="wildcard.media.behavior.Audio"
      for="plone.dexterity.interfaces.IDexterityContent"
      marker="wildcard.media.interfaces.IAudioEnabled"
      />

/home/mcfaddenm/repos/plone5/rfasite/rfasite/rfasite/content/behaviors/audio.py

from plone.autoform.interfaces import IFormFieldProvider
from wildcard.media.behavior import namedfile
from wildcard.media.behavior import valid_audio
from wildcard.media.behavior import IAudio
from rfasite.content import _
from zope.interface import provider

@provider(IFormFieldProvider)
class IRFAAudio(IAudio):
    
    audio_file = namedfile.NamedBlobFile(
        title=_(u"Audio File"),
        description=u"",
        required=False, #this is what we changed from wildcard.media
        constraint=valid_audio
    )

Oh, and don't forget to update your types that use it.

TTW I unchecked the audio behavior and checked the 'RFA Audio' behavior.

Also edited my gs profile for types that use the behavior:

 <!-- <element value="wildcard.media.behavior.IAudio" /> -->
  <element value="rfasite.content.behaviors.audio.IRFAAudio"/>

Now, for the next challenge:

"""
As a behavior modifying developer
I want to hide the old, unmodified behavior from TTW Dexterity Content settings
(and prominently show the new modified behavior in it's place)
So that TTW settings users don't get confused and choose the wrong behavior
"""

To illistrate, I changed my configure.zcml and got the following screenshot.

  <plone:behavior
      title="Audio"
      description="(modified) Add support for audio fields"
      provides="rfasite.content.behaviors.audio.IRFAAudio"
      factory="wildcard.media.behavior.Audio"
      for="plone.dexterity.interfaces.IDexterityContent"
      marker="wildcard.media.interfaces.IAudioEnabled"
      />

Screenshot from 2019-05-15 11-31-29

Maybe the jbot model would work here.

Couldn't you use overrides.zcml in your product? Similar to...

  <configure zcml:condition="installed pp.client.plone.browser"
             package="pp.client.plone.browser">
    <browser:page
        name="asHTML"
        layer="pp.client.plone.interfaces.IPloneClientConnectorLayer"
        for="plone.dexterity.interfaces.IDexterityContainer"
        permission="zope2.View"
        class="mdb_theme.utils.DexterityContentView"
    />
</configure>
1 Like