Search code examples
pythonsqlformsplonedexterity

Dynamically change dropdown options for one field, after setting another field in a dexterity form.Schema


i'm failing at dynamically changing the dropdown options for one field, after setting another field in a dexterity form.Schema.

the vocabularies are based in a sql database.

specifically i want to update the vocabulary options for township after selecting the county.

right now i am just pulling the full list of townships regardless of the country selected. as all attempts to dynamically change things led to errors.

any help on this would be appreciated. thanks!

from site.py:

from selectionProvider import SelectionProvider
vocabProvider = SelectionProvider()

@grok.provider(IContextSourceBinder)
def countySource(context):
    return vocabProvider.getVocabulary('county')

@grok.provider(IContextSourceBinder)
def townshipSource(context):
    return vocabProvider.getVocabulary('township')

class IESurrenderSite(form.Schema):

    county = schema.Choice(
        title=_(u"County"),
        source=countySource,
        required=True,
    )

    township = schema.Choice(
        title=_(u"Township"),
        source=townshipSource,
        required=True,
    )

...

from selectiorProvider.py:

class SelectionProvider(object):

DB = maap_db.maap_db()
vocabularies = {}
Counties = {}
Townships = {}

def getCountiesDropdownRecordSet(self, object):
    """input:object is a string
    """
    print 'in getCountiesDropdownRecordSet'
    self.DB.open()

    rs = self.DB.RunQuery("Select *, CountyID as [value], name as [title] From County Order By name;")

    self.DB.close()
    SelectionProvider.CountiesByName = {}
    for rec in rs:

        SelectionProvider.CountiesByName[rec['CountyID']] = rec['title']
    #

    return rs

def getTownshipDropdownRecordSet(self, object):
    """input:object is a string
    """
    print 'in getTownshipDropdownRecordSet'
    self.DB.open()

    rs = self.DB.RunQuery("Select *, TownshipID as [value], name as [title] From Township Order By name;")

    self.DB.close()
    SelectionProvider.TownshipsByName = {}
    for rec in rs:
        SelectionProvider.TownshipsByName[rec['TownshipID']] = rec['title']
    #

    return rs

# #
def getDropdownRecordSet(self, object):
    """input:object is a string
    """
    print 'in getDropdownRecordSet'
    self.DB.open()
    rs = self.DB.RunQuery("Select * From DropdownSelections Where object = '%s' Order By seqNo;" % (object))
    self.DB.close()

    return rs


def buildVocabulary(self, rs, valueField='value', titleField='title'):
    """DO NOT USE directly outside this class, see getVocabulary() or rebuildVocabulary() instead
    """
    data = []
    for rec in rs:
        data.append(SimpleTerm(value=rec[valueField], title=_(rec[titleField])))
    #
    return SimpleVocabulary(data)
#

def rebuildVocabulary(self, object):
    """Force a fetch from the database and rebuild the vocabulary.
    input object: a string, matches the DropdownSelections field
    """
    print 'initializing %s' % (object)

    if object=="county":
        print 'going to CountiesDropdowns'
        vocab = self.buildVocabulary(self.getCountiesDropdownRecordSet(object), "CountyID","title")
        SelectionProvider.vocabularies[object] = vocab

        return vocab

    if object=="township":
        print 'going to TownshipDropdowns'
        vocab = self.buildVocabulary(self.getTownshipDropdownRecordSet(object), "TownshipID","title")
        SelectionProvider.vocabularies[object] = vocab
        #print _SITE_NAME, '%s selection list initialized.' % (object)
        return vocab     
    else:

        vocab = self.buildVocabulary(self.getDropdownRecordSet(object))
        SelectionProvider.vocabularies[object] = vocab
        return vocab


def getVocabulary(self, object):
    """Retrieve cached vocabulary
    input object: a string, matches the DropdownSelections field
    """
    recreate = False
    if not SelectionProvider.vocabularies.has_key(object):
        recreate = True
    #
    vocab = SelectionProvider.vocabularies.get(object)
    if vocab == None or len(vocab) == 0:
        recreate = True
    #
    if recreate:
        vocab = self.rebuildVocabulary(object)
    #
    return vocab

Solution

  • You can do this with plone.formwidget.masterselect Like this (untested, but gives you an idear of how it works):

    from zope import schema
    from plone.supermodel import model
    from plone.formwidget.masterselect import _
    from plone.formwidget.masterselect import MasterSelectBoolField
    from plone.formwidget.masterselect import MasterSelectField
    
    
    def getTownshipDynVocab(master):
        CountryID = master_value
    
        # search for your township entries by CountryID
        # and return it as a DisplayList
        return townshipDynamicVocab
    
    
    class IESurrenderSite(model.Schema):
    
        county = MasterSelectField(
            title=_(u"County"),
            source=countySource,
            slave_fields=(
                # Controls the vocab of township
                {'name': 'township',
                 'action': 'vocabulary',
                 'vocab_method': getTownshipDynVocab,
                },
            ),
            required=True,
        )
    
        township = schema.Set(
            title=_(u"Township"),
            value_type=schema.Choice(),
            required=False,
        )