How to configure a slave field with plone.formwidget.masterselect

I'm working on a behavior that will add some fields to a content type; 2 of these fields (document_type and document_subtype) are used to classify an item.

the document_subtype field could have different values depending on the value of the document_type field; I'm trying to do this using plone.formwidget.masterselect but I found the documentation pretty confusing.

for instance:

  • document_type can be A, B or C
  • if document_type is A, then document_subtype could be A1, A2 or A3
  • if document_type is B, then document_subtype could be B1 or B2
  • if document_type is C, then document_subtype could be C1, C2, C3 or C4

I want to use named vocabularies if possible.

can someone give me a hint?

It's been some time since I last used it, but looking at the demo I would guess you could do it so (untested):

def getSlaveVocab(master):
    if master == 'A'
        return ['A1', 'A2', 'A3', ]
    elif master == 'B':
        return ['B1', 'B2', ]
    elif master == 'C':
        return ['C1', 'C2', 'C3', 'C4', ]
    return ['should', 'not', 'happen', ]


class IMasterSelectDemo(model.Schema):

    masterField = MasterSelectField(
        title=_(u'MasterField'),
        values=('A', 'B', 'C', ),
        slave_fields=(
            # Controls the vocab of slaveField1
            {'name': 'slaveField1',
             'action': 'vocabulary',
             'vocab_method': getSlaveVocab,
             'control_param': 'master',
             },
        ),
        required=True,
    )

    slaveField1 = schema.Set(
        title=_(u'SlaveField1'),
        value_type=schema.Choice(values=('will', 'be', 'replaced')),
        required=False,
    )

my bad, now I see that plone.formwidget.masterselect is Archetypes-based; do we have an option for Dexterity?

Where do you see that?

setup(
    name='plone.formwidget.masterselect',
    version=version,
    description='A z3c.form widget that controls the vocabulary or '
                'display of other fields on an edit page',
    install_requires=[
        'z3c.form',
        'setuptools',
        'plone.supermodel',
        'plone.z3cform',
        'simplejson',
    ],
    extras_require={
        'test': [
            'plone.app.testing [robot] >= 4.2.2',
            'plone.app.robotframework',
            'plone.app.dexterity',
        ],
    },

z3c.form, plone.app.dexterity, I don't see any Archetypes :wink:

Did you get confused with Products.MasterSelectWidget maybe? This one is Archetypes...

my bad again, I'm misunderstanding everything... anyway, when I test your solutions I get:

2017-06-26 13:43:26 ERROR Zope.SiteErrorLog 1498495406.50.503926074686 http://localhost:8080/Plone/library/++add++File/++widget++ILibrary.document_type/@@masterselect-jsonvalue
Traceback (innermost last):
  Module ZPublisher.Publish, line 138, in publish
  Module ZPublisher.mapply, line 77, in mapply
  Module ZPublisher.Publish, line 48, in call_object
  Module plone.formwidget.masterselect.widget, line 251, in __call__
ValueError: Can not find widget: document_subtype

Could you please post your configuration?

sure:

# -*- coding: utf-8 -*-
from my.package import _
from plone.autoform.interfaces import IFormFieldProvider
from plone.formwidget.masterselect import MasterSelectField
from plone.supermodel import model
from zope import schema
from zope.interface import provider


def get_dynamic_subtypes_vocabulary(master):
    print master
    # import pdb; pdb.set_trace()
    # return 'my.package.DocumentSubtypes'
    if master == 'atas':
        return ['A1', 'A2', 'A3', ]
    elif master == 'publicacoes':
        return ['B1', 'B2', ]
    elif master == 'legislacao':
        return ['C1', 'C2', 'C3', 'C4', ]
    return ['should', 'not', 'happen', ]


@provider(IFormFieldProvider)
class ILibrary(model.Schema):
    """Library behavior."""

    activity = schema.Choice(
        title=_(u'Activity'),
        vocabulary='my.package.Activities',
        required=True,
    )

    document_type = MasterSelectField(
        title=_(u'Document Type'),
        vocabulary='my.package.DocumentTypes',
        slave_fields=(
            {
                'name': 'document_subtype',
                'action': 'vocabulary',
                'vocab_method': get_dynamic_subtypes_vocabulary,
                'control_param': 'master',
            },
        ),
        required=True,
    )

    document_subtype = schema.Set(
        title=_(u'Document Subtype'),
        value_type=schema.Choice(values=('will', 'be', 'replaced')),
        required=False,
    )

as I mentioned, I would like to use named vocabularies as the options would be in Portuguese language.

Hmm, what versions are you using?

I can't test right now, but I will do so tomorrow morning.

One thing that you could try regarding your vocabularies: If the return value of self.getVocabulary is already a vocabulary, and not a list/tuple, everything is fine.

So this could work:

def get_dynamic_subtypes_vocabulary(master):
    print master
    # import pdb; pdb.set_trace()
    # return 'my.package.DocumentSubtypes'
    if master == 'atas':
        return MyVocabulary()
    elif master == 'publicacoes':
        return MyOtherVocabulary()
    elif master == 'legislacao':
        return YetAnotherVocabulary()
    return ['should', 'not', 'happen', ]

man, I just discovered that I don't need the slave field at all.

I'm really stressed today because of the deadlines; is just Monday and I want my weekend back :wink:

thank you!

PS: anyway I think the use case is valid and it would be nice to fix the documentation.

UPDATE: I was using Plone 4.3.14, plone.app.contenttypes 1.1.1 and plone.formwidget.masterselect 1.6.

according to @keul the add-on is broken right now:

once again I stumbled upon this use case and the master select widget is still not working for Dexterity-based content types.

it seems I found the issue: you have to set the slaveID parameter; IIRC, Archetypes and Dexterity default field names are different.

for instance, if your field name is foo, you have to use:

{
    'name': 'foo',
    'slaveID': '#formfield-form-widgets-foo',
    'action': 'hide',
    'hide_values': ('bar',),
},