pythonplonezopeploneformgenz3c.form

How to format an integer widget on the controlpanel using plone.app.registry


I have an add-on configuration page/form generated by plone.app.registry.browser.controlpanel

Using these Docs:
http://plone.org/documentation/kb/how-to-create-a-plone-control-panel-with-plone.app.registry https://pypi.python.org/pypi/plone.app.registry#control-panel-widget-settings

On this form, I have an integer field:

from zope import schema
from plone.app.registry.browser import controlpanel

class MyAddonSettings(Interface):
    partnerId = schema.Int(title=u"Partner Id",
                           description=u"enter your Partner ID",
                           required=True,
                           default=54321)

class SettingsEditForm(controlpanel.RegistryEditForm):
    schema = MyAddonSettings
    label = u"My settings"
    description = u""""""

    def updateFields(self):
        super(SettingsEditForm, self).updateFields()    

    def updateWidgets(self):
        super(SettingsEditForm, self).updateWidgets()    

class SettingsControlPanel(controlpanel.ControlPanelFormWrapper):
    form = SettingsEditForm

When the form renders, I get the integer field auto-filled with '54,321' I don't want the comma.

How to I specify "Don't do that!"


Solution

  • So, I think I went pretty deep down the rabbit hole, but here is what I came up with.

    1) The default widget for zope.schema.Int is the TextWidget

    2) z3c.form.converter.IntegerDataConverter Adapts itself to zope.schema.interfaces.IInt and ITextWidget

    3) the IntegerDataConverter calls upon the locale to 'format the integer' for you, giving you a nice pretty representation of an int - with commas.

    My choice was to create a new widget 'IntWidget' and a new converter 'NoFormatIntegerDataConverter', adapt these. Then manually set the field in question to my new widget:

    I'm sure there is a less 'rabbit hole' way to do this, but I found myself at the bottom, so I completed the journey. I'll let a zope guru follow up with the 'right' way to do it.

    =========================

    create the new widget based on TextWidget

    so we don't tie our new converter to everyone's TextWidget and break someone else's stuff

    import zope.interface
    import zope.component
    import zope.schema.interfaces
    
    import z3c.form.interfaces
    from z3c.form.widget import FieldWidget
    from z3c.form.browser.text import TextWidget
    from z3c.form import converter
    
    class IIntWidget(z3c.form.interfaces.ITextWidget):
        """Int Widget"""
    
    class IntWidget(TextWidget):
        zope.interface.implementsOnly(IIntWidget)
        klass = u'int-widget'
        value = u''
    
    @zope.component.adapter(zope.schema.interfaces.IField, 
                            z3c.form.interfaces.IFormLayer)
    @zope.interface.implementer(z3c.form.interfaces.IFieldWidget)
    def IntFieldWidget(field, request):
        """IFieldWidget factory for IntWidget."""
        return FieldWidget(field, IntWidget(request))
    
    zope.component.provideAdapter(IntFieldWidget)
    

    Create the 'dumb' converter, and adapt it to our new widget 'IntWidget'

    class NoFormatIntegerDataConverter(converter.IntegerDataConverter):
        """ data converter that ignores the formatter, 
            simply returns the unicode representation of the integer value
    
            The base class for this calls upon the locale for a formatter.
            This completely avoids calling the locale.
        """
    
        zope.component.adapts(zope.schema.interfaces.IInt, IIntWidget)    
    
        def toWidgetValue(self, value):
            if value is self.field.missing_value:
                return u''
            #go look at z3c.form.converter.IntegerDataConverter 
            #to see what it used to return here.
            return unicode(value)  
    
    zope.component.provideAdapter(NoFormatIntegerDataConverter)
    

    Finally, update the field widget factory to use our new widget

    class SettingsEditForm(controlpanel.RegistryEditForm):
    
        ...
    
        def updateFields(self):
            super(SettingsEditForm, self).updateFields()
            self.fields['partnerId'].widgetFactory = IntFieldWidget  #<----- Here
    
        ...