Help - generating options of 'select' field base on value of another 'select' field of content type?

hi all,
i have a content type DeptPage, which has 2 'select' fiels:
dept_cat and dept_id. Selection of dept_id depends on dept_cat.

how can i get the value of dept_cat in 'source' function for dept_id?

@implementer(IContextSourceBinder)
class Get_dept_ids(object):
    """
    return list of dept ids belong to a categorie 
    """
    def __call__(self, context):
        # how to get value of 'dept_cat' in edit/add form?
        pass

class IDeptPage(model.Schema):  
    directives.widget(dept_cat=SelectFieldWidget)
    dept_cat = schema.Choice(
        title=_(u'Categorie'), 
        source=Get_cat_ids(),
        required=True,
        )

    directives.widget(dept_id=SelectFieldWidget)
    dept_id = schema.Choice(
        title=_(u'Dept'), 
        source=Get_dept_ids(),
        required=True,
        )

Check plone.formwidget.masterselect:

hi, at the Demo plone.formwidget.masterselect/src/plone/formwidget/masterselect/demo.py at master · collective/plone.formwidget.masterselect · GitHub
i see master and slave field have only simple list of values.
I need to have select fields with value and title so I tried this:

@implementer(IContextSourceBinder)
class get_lifesit_cat(object):    
    def __call__(self, context):        
        return SimpleVocabulary(
            [
                SimpleTerm(value='platinum', title='Platinum Sponsor'),
                SimpleTerm(value='gold', title='Gold Sponsor'),
            ]
        )


def getSlaveVocab(master):
    if master == 'platinum':
        return SimpleVocabulary(
            [
                SimpleTerm(value='platinum 1', title='Platinum Sponsor 1'),
                SimpleTerm(value='platinum 2', title='Platinum Sponsor 2'),
            ]
        )
    elif master == 'gold':
        return SimpleVocabulary(
            [
                SimpleTerm(value='gold 1', title='Gold Sponsor 1'),
                SimpleTerm(value='gold 2', title='Gold Sponsor 2'),
            ]
        )

    return SimpleVocabulary([])
    
#test MasterField
class ILifeSituationHelper(model.Schema):
    """LifeSituationHelper"""

    #master
    directives.write_permission(lifesit_categories='cmf.ModifyPortalContent')
    directives.widget(lifesit_categories=SelectFieldWidget)
    lifesit_categories = MasterSelectField(
        title=_(u'Category'),
        description=u'',
        source=get_lifesit_cat(),
        required=True,

        slave_fields=(
            # Controls the vocab of slaveField1
            {'name': 'life_sit',
             'action': 'vocabulary',
             'vocab_method': getSlaveVocab,
             'control_param': 'master',
            },        
        ),
    )

    #slave
    directives.write_permission(life_sit='cmf.ModifyPortalContent')
    directives.widget(life_sit=SelectFieldWidget)
    life_sit = schema.Choice(
        title=_(u'Situation'),
        description=u'',        
        values=['will', 'be', 'replaced'],
        #source=dummy_ls(),
        required=True,
    )

but when you select 'gold' in master field the slave keeps unchange :frowning:

  1. any idea with this package plone.formwidget.masterselect?

I also tried with Ajax.
Ajax set options of slave field correctly, but as my slave field is schema.choice,
Plone requeries setting of 'values' or 'source' of slave field definition,

so when Ajax sets new values for slave field, @@z3cform_validate_field is called
it say slave field has not value from 'values' or 'source'
eggs/zope.schema-6.2.0-py3.8.egg/zope/schema/vocabulary.py", line 203, in getTermByToken
raise LookupError(token)
LookupError: platinumGreen

  1. I dont know how to overwrite z3cform_validate_field :frowning:

Many thanks for your time.

We usually do this using "vocabulary" and not "source" and it works as expected:

from zope.interface import Interface, implementer
from zope import schema
from plone.formwidget.masterselect import MasterSelectField
from your.addon import _
from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary


## Vocabulary


class VocabItem(object):
    def __init__(self, token, value):
        self.token = token
        self.value = value


@implementer(schema.interfaces.IVocabularyFactory)
class YourCodesVocabulary(object):
    """ """

    def __call__(self, context):
        # Just an example list of content for our vocabulary,
        # this can be any static or dynamic data, a catalog result for example.
        items = [
            VocabItem("code1", _("Code 1")),
            VocabItem("code2", _("Code 2")),
            VocabItem("code3", _("Code 3")),
            VocabItem("code4", _("Code 4")),
            VocabItem("code5", _("Code 5")),
        ]

        # create a list of SimpleTerm items:
        terms = []
        for item in items:
            terms.append(
                SimpleTerm(
                    value=item.token,
                    token=str(item.token),
                    title=item.value,
                )
            )
        # Create a SimpleVocabulary from the terms list and return it:
        return SimpleVocabulary(terms)


YourCodesVocabularyFactory = YourCodesVocabulary()

"""
Add the following in the ZCML file:

    <utility
        component=".file.YourCodesVocabularyFactory"
        name="your.vocabulary.codes"
    />

"""


## interface


class MyInterface(Interface):
    master_code = MasterSelectField(
        title=_("master_code"),
        vocabulary="your.vocabulary.codes",
        default="",
        slave_fields=(
            # Controls the vocab of slaveField1
            {
                "name": "sub_code",
                "action": "hide",
                "hide_values": "code1",  # hide the field when the selected value is code1
                "siblings": True,
            },
            {
                "name": "other_field",
                "action": "hide",
                "hide_values": "code2",  # hide the field when the selected value is code2
                "siblings": True,
            },
        ),
        required=False,
    )

    sub_code = schema.TextLine(
        title=_(
            "sub_code",
        ),
        default="",
        required=False,
        readonly=False,
    )

    other_field = schema.TextLine(
        title=_(
            "other_field",
        ),
        default="",
        required=False,
        readonly=False,
    )


Thx Mikel for the code.

Do you know any similar package for Plone 6? or I have to write Ajax for Plone 6? :slight_smile: