Field widget for TreeVocabulary [SOLVED]

Hi all,

I am unable to render a field for TreeVocabulary.fromDict(my_dict) in Plone 5.0.7 using these examples


using - Traceback at bottom...

also, the AjaxSelectFieldWidget only displays the top level terms, as received from @@getVocabulary?name=mdb_theme.TestDynamicTree&field=test_tree_vocab

As a possible alternative, I looked at collective.dynatree (as suggested in collective.vdexvocabulary) but this appears to be a Plone4 project and relying on a custom js framework. There is a mockup pattern which looks nice, is there a fieldwidget implementation of ?

Any other options?

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.z3cform.layout, line 66, in __call__
  Module plone.z3cform.layout, line 60, in update
  Module z3c.form.form, line 162, in render
  Module zope.browserpage.viewpagetemplatefile, line 51, in __call__
  Module zope.pagetemplate.pagetemplate, line 132, in pt_render
  Module, line 98, in __call__
  Module, line 163, in render
  Module chameleon.zpt.template, line 261, in render
  Module chameleon.template, line 171, in render
  Module, line 91, in render
  Module, line 1809, in render_titlelessform
  Module, line 811, in render_fields
  Module, line 126, in render_widget_rendering
  Module, line 1070, in render_field
  Module, line 161, in __call__
  Module Products.Five.browser.metaconfigure, line 485, in __call__
  Module zope.browserpage.viewpagetemplatefile, line 83, in __call__
  Module zope.browserpage.viewpagetemplatefile, line 51, in __call__
  Module zope.pagetemplate.pagetemplate, line 132, in pt_render
  Module, line 98, in __call__
  Module, line 163, in render
  Module chameleon.zpt.template, line 261, in render
  Module chameleon.template, line 191, in render
  Module chameleon.template, line 171, in render
  Module, line 610, in render
  Module, line 481, in render_widget_wrapper
  Module, line 161, in __call__
  Module, line 120, in render
  Module, line 178, in __init__
  Module, line 202, in _set_items
ValueError: too many values to unpack

 - Expression: "widget/@@ploneform-render-widget"
 - Filename:   ... emplates/overrides/
 - Location:   (line 99: col 81)
 - Source:     ... place="structure widget/@@ploneform-render-widget"/>
 - Expression: "widget/render"
 - Filename:   ... rm-3.0.9-py2.7.egg/plone/app/z3cform/templates/
 - Location:   (line 39: col 46)
 - Source:     ... xt" tal:replace="structure widget/render"
 - Arguments:  repeat: {...} (0)
               context: <SelectWidget test_tree_vocab at 0x7f897750cc10>
               views: <ViewMapper - at 0x7f8977463a90>
               modules: <TraversableModuleImporter - at 0x7f89897d1350>
               args: <tuple - at 0x7f899228d050>
               nothing: <NoneType - at 0x91a870>
               target_language: <NoneType - at 0x91a870>
               default: <object - at 0x7f89921a9530>
               request: <instance - at 0x7f897e8a3440>
               wrapped_repeat: {...} (0)
               loop: {...} (0)
               template: <ViewPageTemplateFile - at 0x7f89771b67d0>
               translate: <function translate at 0x7f8977452a28>
               options: {...} (0)
               view: <RenderWidget ploneform-render-widget at 0x7f89770a6490>

Might want to look at collective.taxonomy? Though the widget could need some Love...

Thanks @petschki - I actually tried collective taxonomy before going with the homebrew TreeVocabulary approach. There are many things I like about it - including the TTW editing ability.

A blocker for me is the way the translation of terms is stored. We manage all our other translated terms (headings, field content) with Pootle and sync those back to Plone .po files. Then the TAL templates take care of displaying content in the requested language.

Edit to add, plus I need the term tokens to be predictable, as they are used in other parts of my application.

Meanwhile, I was able to pin these:

# p.a.w 2.4.1 provides support for optgroup in select forms = 2.4.1
# p.a.c. 3.5.4. is the last one before refactoring getObjSize (human_readable_size) = 3.5.4
# try bleeding edge = 3.0.9

and reach a potentially adequate solution: using a SelectWidget, I can display/edit all options in an optgroup (e.g. second-level)

    <field name="test_tree_vocab" type="zope.schema.Set" lingua:independent="true">
      <form:widget type="" />
      <value_type type="zope.schema.Choice">
      <description>Tree selector</description>

One caveat, the new pins brought back to life an issue with fields which use RelationList or RelationChoice objects, probably the querysource rooted wrong. Will attempt to tackle that after the weekend...

Also, collective.taxonomy does not uninstall properly. Found myself with a dangling persistent utility
AttributeError: type object 'ITaxonomy' has no attribute '__iro__' even after removing my test taxonomy from the registry before uninstalling...

For big projects, I suggest to use an external vocabulary server. For example:

This can also help you to reuse vocabularies and ontologies, and be ready for semantic web, If you need it.

Update, I finally gave up on using a single widget. My use case can also be resolved with collective.z3cform.datagridfield - using a master and slave field. The vocabulary in the slave field gets filtered by the master value whenever the master field changes.

At some point in the future, I might take a better look at collective.dynatree to see if it could be ported to Plone 5.

Update 2
Icouldn't let this go, and ended up updating Mikko Ohtamaa's dgftreeselect widget for Plone5, this was quite easy to do. I also needed the json used in the example widget to be dynamic, and ended up subclassing VocabularyView from

so with this as an interface,

class IPackagingRowSchema(Interface):
    packaging_unit_key = schema.TextLine( title   = _(u'Mengeneinheit')
                                        , required=True
    packaging_unit_value = schema.TextLine( title = _(u'Gebindegröße')
                                          , required=True
    directives.widget('packaging_unit_key',   DGFTreeSelectFieldWidget)
    directives.widget('packaging_unit_value', DGFTreeSelectFieldWidget)
IPackagingRowSchema.setTaggedValue("vocabulary", 'my.product.CustomTreeVocabulary')

and this view

# -*- coding: utf-8 -*-
from import BaseVocabularyView
from import VocabularyView
from zope.schema.interfaces import ITreeVocabulary
from collections import OrderedDict

import json

class TreeVocabularyView(BaseVocabularyView):

    def __call__(self):
        Accepts GET parameters of:
        name: Name of the vocabulary
        field: Name of the field the vocabulary is being retrieved for

        context = self.context
        request = self.request

            'Content-Type', 'application/json; charset=utf-8'

        vocabulary = VocabularyView(self, request).get_vocabulary()

        if ITreeVocabulary.providedBy(vocabulary):
            level = 0
            items = get_terms(vocabulary, level, [])

            items = [{ 'id'    : term.token
                     , 'label' : term.title
                     } for term in vocabulary]

        return json.dumps(items)

def get_terms(vocabulary, level, terms):
    for term, child_terms in vocabulary.items():
        item = OrderedDict()

        item['id'] = term.token
        item['label'] = term.title

        if child_terms and level < 10:
            level += 1
            item['children'] = get_terms(child_terms, level, [])


    return terms

the dgftreewidget can use


and get its data...