How to override or inherit a z3c.form widget?

238 Views Asked by At

I need to override the behavior of the widget assigned to a field on a z3c.form (specifically the widget used for a schema.Bool field like the one declared below) to change the way the widget mode works under different conditions:

from plone.autoform.interfaces import IFormFieldProvider
from plone.supermodel import model
from zope import schema
from zope.interface import provider

@provider(IFormFieldProvider)
class IGoogleNews(model.Schema):
    """Behavior interface to add some Google News features."""

    standout_journalism = schema.Bool(
        title=_(u'Standout Journalism'),
        required=False,
    )

    news_keywords = schema.Tuple(
        title=_(u'Keywords'),
        value_type=schema.TextLine(),
        required=False,
    )
1

There are 1 best solutions below

1
Mathias On

This is example code derived from ftw.datepicker widget.

In widget.py:

# This widget has a plone integration, so we take this one as base
from plone.z3cform.widget import SingleCheckBoxWidget
from z3c.form.interfaces import DISPLAY_MODE
from z3c.form.interfaces import ISingleCheckBoxWidget
from zope.interface import implementsOnly
from zope.component import adapter
from z3c.form.interfaces import IFormLayer
from z3c.form.interfaces import IFieldWidget
from z3c.form.widget import FieldWidget


class IMyBoolWidget(ISingleCheckBoxWidget):
    """Marker interface"""


class MyBoolWidget(SingleCheckBoxWidget)
    implementsOnly(IMyBoolWidget)

    display_template = ViewPageTemplateFile('templates/keyword_display.pt')
    input_template = ViewPageTemplateFile('templates/keyword_input.pt')
    hidden_template = ViewPageTemplateFile('templates/keyword_hidden.pt')
    disabled_template = ViewPageTemplateFile('templates/disabled_hidden.pt') 

    def update(self):
       super(MyBoolWidget, self).update()

    if self.disable_widget():
        # Change widget to display mode
        self.mode = 'my_own_disabled_mode'

    def disable_widget(self):
        # Figure out if the widget should enabled or not.

   def render(self):
       if self.mode == INPUT_MODE:
            return self.input_template(self)
        elif self.mode == DISPLAY_MODE:
            return self.display_template(self)
        elif self.mode == HIDDEN_MODE:
            return self.hidden_template(self)
        elif self.mode == 'my_own_disabled_mode'
            return self.disabled_template(self)
        raise NotImplementedError(
            'Mode: "{0}" not supported'.format(self.mode))

# Following the patterns of z3c widget and create a widget factory.
# This is not necessary in this case, since there are no additional params.

@adapter(IMyBoolWidget, IFormLayer)
@implementer(IFieldWidget)
def MyBoolWidgetFactory(field, request, config=None):
    """IFieldWidget factory for..."""
    return FieldWidget(field, MyBoolWidget(request))

MyBoolFieldWidget = MyBoolWidgetFactory

Register using zcml (configure.zcml):

    <adapter factory=".widget.MyBoolWidgetFactory" />

Use the widget for your boolean field.

from plone.autoform.interfaces import IFormFieldProvider
from plone.supermodel import model
from zope import schema
from zope.interface import provider
from plone.directives import form
from wherever.the.widget.is import widget MyBoolFieldWidget


@provider(IFormFieldProvider)
class IGoogleNews(model.Schema):
    """Behavior interface to add some Google News features."""

    form.widget('standout_journalism', MyBoolFieldWidget)
    standout_journalism = schema.Bool(
        title=_(u'Standout Journalism'),
        required=False,
    )

    news_keywords = schema.Tuple(
        title=_(u'Keywords'),
        value_type=schema.TextLine(),
        required=False,
    )

Following this approach I created several widgets, For example also a KeywordWidget

On the plus side:

  • Usually small and minimalistic
  • easy to use

malus:

  • You need to manually apply it everywhere you want.

The example does not include any templates, you may get them from z3c.form or plone.z3cform.*

This approach is pretty straight forward and does not use the all the components a widget usually does. Like register a widget template for every mode, etc.