Moving behavior fields to different fieldset?

I have a custom DX content-types and collective.geolocationbehavior added to the CT through its FTI.

Now the geolocation field shows up default fieldset in edit mode. I need to move it to a different fieldset or its own fieldset.

Using this

fieldset('MyField', fields=['geolocation'])

inside my schema does work:

zope.configuration.config.ConfigurationExecutionError: <type 'exceptions.ValueError'>: The directive plone.supermodel.fieldsets applied to interface dynamore.policy.content.dynalocation.IDynalocation refers to unknown field name geolocation
  File "/home/ajung/sandboxes/plone-server-buildout-plone5/eggs/plone.supermodel-1.3.4-py2.7.egg/plone/supermodel/configure.zcml", line 9.4-12.10

You can do this globally by moving the fields for all CTs using it (example showing the changeNote field, but can be applied to all other behaviors as well):

from import _ as _DX
from import IVersionable
from plone.supermodel.interfaces import FIELDSETS_KEY
from plone.supermodel.model import Fieldset

settings = Fieldset(
fieldsets = IVersionable.getTaggedValue(FIELDSETS_KEY)
1 Like

Your (magic) example code works great! I initially thought it belonged in the class definition for the content type's interface but, in fact, it belongs outside of any class definition.

But I could not get it to work with lead image behaviour. It would result in this error. I don't know why ILeadImage would be different.

KeyError: 'Plone.supermodel.fieldsets'

when reaching the line

fieldsets = ILeadImage.getTaggedValue(FIELDSETS_KEY)

The way I found that worked was to override the two lead image classes with my own file:

# -*- coding: utf-8 -*-
from import _
from plone.autoform.interfaces import IFormFieldProvider
from import ILeadImage
from plone.dexterity.interfaces import IDexterityContent
from plone.supermodel import model
from zope.component import adapter
from zope.interface import implementer
from zope.interface import provider

class IOIELeadImage(ILeadImage):
        label=_(u'Lead Image'),
        fields=['image', 'image_caption'],

class LeadImage(object):

    def __init__(self, context):
        self.context = context

and then using that behaviour in the content type XML definition instead.

...ok maybe I spoke too behaves correctly in the add form but isn't working when saving

[a moment passes]
To go down this route and have it work, I had to register my new customized behaviour.

But @tmassman's (much simpler and more elegant) original suggestion works for lead images if you do something like this:

leadimage_fieldset = Fieldset(
    label=_(u'Lead Image'),
    fields=['image', 'image_caption'],
    leadimage_fieldsets = ILeadImage.getTaggedValue(FIELDSETS_KEY)
except KeyError:
    ILeadImage.setTaggedValue(FIELDSETS_KEY, [leadimage_fieldset])

I know ILeadImage defines no fieldsets because calling ILeadImage.getTaggedValue(FIELDSETS_KEY) gives a KeyError. I used a try block to be more general here.

This should be added to the docs... and

Warming up an old thread to dump my working code snippet here:

from plone.supermodel.interfaces import FIELDSETS_KEY
from plone.supermodel.model import Fieldset

def move_field(
    schema, fieldname, to_fieldset_name, label=None, description=None, order=None
    """Moves a field named "fieldname" on a Zope "schema" to a new fieldset "to_field_name".

    - creates a new fieldset on demand (then label, description and order are passed to the new one).
    - if value of "to_fieldset_name" is "default", then the field sticks on the main form.
    # find schema with field in inheritance tree
    schema_with_field = None
    for pschema in reversed(schema.__iro__):
            schema_with_field = pschema
    if schema_with_field is None:
        raise KeyError(f"field '{fieldname}' does not exist on {schema}.")

    # remove field from fieldset (if in any)
    fieldsets_direct = schema_with_field.queryDirectTaggedValue(FIELDSETS_KEY)
    if fieldsets_direct is not None:
        for fieldset in fieldsets_direct:
            if fieldname in fieldset.fields:

    if to_fieldset_name == "default":
        # default means to fieldset, but on main form

    if fieldsets_direct is None:
        # no tagged value set so far!
        fieldsets_direct = list()
        schema.setTaggedValue(FIELDSETS_KEY, fieldsets_direct)

    # try to find the fieldset, append and exit
    for fieldset in fieldsets_direct:
        if fieldset.__name__ == to_fieldset_name:

    # not found, need to create new fieldset
    new_fieldset = Fieldset(
    if label is not None:
        new_fieldset.label = label
    if description is not None:
        new_fieldset.description = description
    if order is not None:
        new_fieldset.order = order
    # Done!

# example
from import ILeadImageBehavior
from import ICategorization
from import IDublinCore

# 1) move direct field language on ICategorization from fieldset
#    categorization to settings
move_field(ICategorization, "language", "settings")

# 2) move inherited field subject on IDublinCore from fieldset
#    categorization to settings
move_field(IDublinCore, "subjects", "settings")

# 3) move inherited field image on ILeadImage from main form
#    to new fiedset images
move_field(ILeadImageBehavior, "image", "images", label="Images", order=10)

We may want to have this in the of plone.supermodel or plone.autoform I suppose. Any volunteers?


Plone Foundation Code of Conduct