There is no persistent field equivalent for the field 'A' of type `B`

A and B for representation purposes of-course. This is an issue I've been trying to figure out, I have a dynamic vocabulary for a settings configlet, however it always fails with the above error when trying to install my package.
Specifically it fails on the 'ecwid_priority' schema field.

Note: I've censored some content and names out, for all intents and purposes 'somesite' is the subject and package...

The error:

Traceback (innermost last):
  Module ZPublisher.Publish, line 138, in publish
  Module ZPublisher.mapply, line 77, in mapply
  Module Products.PDBDebugMode.runcall, line 70, in pdb_runcall
  Module ZPublisher.Publish, line 48, in call_object
  Module <string>, line 6, in reinstallProducts
  Module AccessControl.requestmethod, line 70, in _curried
  Module Products.CMFQuickInstallerTool.QuickInstallerTool, line 784, in reinstallProducts
  Module <string>, line 3, in installProducts
  Module AccessControl.requestmethod, line 70, in _curried
  Module Products.CMFQuickInstallerTool.QuickInstallerTool, line 686, in installProducts
  Module Products.CMFQuickInstallerTool.QuickInstallerTool, line 602, in installProduct
   - __traceback_info__: ('somesite.website',)
  Module Products.GenericSetup.tool, line 388, in runAllImportStepsFromProfile
   - __traceback_info__: profile-somesite.website:default
  Module Products.GenericSetup.tool, line 1433, in _runImportStepsFromContext
  Module Products.GenericSetup.tool, line 1245, in _doRunImportStep
   - __traceback_info__: plone.app.registry
  Module plone.app.registry.exportimport.handler, line 70, in importRegistry
  Module plone.app.registry.exportimport.handler, line 118, in importDocument
  Module plone.app.registry.exportimport.handler, line 385, in importRecords
   - __traceback_info__: records name: somesite.website.settings.settings.ISettings
  Module plone.registry.registry, line 104, in registerInterface
TypeError: There is no persistent field equivalent for the field `ecwid_priority` of type `List`.
[17] > /home/jesse/.buildout/eggs/plone.registry-1.0.5-py2.7.egg/plone/registry/registry.py(104)registerInterface()
-> field.__class__.__name__

Here is my settings schema:

# -*- coding: utf-8 -*-

from somesite.website import _
from somesite.website.utils import get_ecwid_data
from plone.app.registry.browser.controlpanel import ControlPanelFormWrapper
from plone.app.registry.browser.controlpanel import RegistryEditForm
from plone.app.z3cform.widget import AjaxSelectFieldWidget
from plone.autoform import directives
from plone.registry import Registry
from plone.supermodel.directives import fieldset
from plone.z3cform import layout
from zope import schema
from zope.interface import directlyProvides
from zope.interface import Interface
from zope.schema.interfaces import IContextSourceBinder
from zope.schema.vocabulary import SimpleTerm
from zope.schema.vocabulary import SimpleVocabulary


SHOP_STATUSES = SimpleVocabulary(
    [SimpleTerm(value=u'open', title=_(u'Open')),
     SimpleTerm(value=u'auto', title=_(u'Auto')),
     SimpleTerm(value=u'closed', title=_(u'Closed'))]
)


def possibleProducts(context):
    terms = [SimpleTerm(value=empty, title=u'Empty')]
    products = []
    data = get_ecwid_data()
    if data:
        items = data['items']
        for item in items:
            products.append(
                {'id': item['id'],
                 'name': item['name']}
            )
        for product in products:
            terms.append(SimpleTerm(value=product['id'],
                                    title=product['name']))
    return SimpleVocabulary(terms)


directlyProvides(possibleProducts, IContextSourceBinder)

registry = Registry()


class ISettings(Interface):
    """Controlpanel fields"""

    fieldset(_(u'settings_fieldset_shop',
               default=u'Shop'), fields=('shop_status',
                                         'shop_status_note',
                                         'shop_opening_time',
                                         'shop_closing_time',
                                         'shop_email',
                                         'shop_phone',
                                         'shop_address'))

    shop_status = schema.Choice(
        title=_(u'settings_shop_status_title',
                default=u'Shop Status'),
        description=_(u'settings_shop_status_description',
                      default=u'Select whether or not the shop is currently open, closed or automatically set.'),  # noqa
        vocabulary=SHOP_STATUSES,
        default=u'auto',
        required=False,
    )

    shop_status_note = schema.TextLine(
        title=_(u'settings_shop_status_note_title',
                default='Shop Status-Note'),
        required=False,
    )

    shop_opening_time = schema.TextLine(
        title=_(u'settings_shop_opening_time_title',
                default='Shop Opening-Time'),
        required=False,
        default=u'9 Uhr',
    )

    shop_closing_time = schema.TextLine(
        title=_(u'settings_shop_closing_time_title',
                default='Shop Closing-Time'),
        required=False,
        default=u'18 Uhr',
    )

    shop_email = schema.TextLine(
        title=_(u'settings_shop_email_title',
                default='Shop E-Mail'),
        required=False,
        default=u'@somesite.de',
    )

    shop_phone = schema.TextLine(
        title=_(u'settings_shop_number_title',
                default='Shop Phone'),
        required=False,
        default=u'',
    )

    shop_address = schema.TextLine(
        title=_(u'settings_shop_address_title',
                default='Shop Address'),
        required=False,
        default=u'',
    )

    fieldset(_(u'settings_fieldset_ecwid',
               default=u'Ecwid'), fields=('ecwid_updated',
                                          'ecwid_data',
                                          'ecwid_priority'))

    ecwid_updated = schema.Datetime(
        title=_(u'settings_ecwid_updated_title',
                default='Ecwid Date'),
        description=_(u'settings_ecwid_updated_description',
                      default=u"Date when Ecwid data was last requested."),
        required=False,
    )

    ecwid_data = schema.Text(
        title=_(u'settings_ecwid_data',
                default='Ecwid Data'),
        required=False,
    )

    ecwid_priority = schema.List(
        title=_(u'settings_ecwid_priority_title',
                default=u'Priority Products'),
        description=_(u'settings_ecwid_priority_description',
                      default=u"Display priority products on the homepage."),
        value_type=schema.Choice(source=possibleProducts),
        required=False,
    )

    directives.widget(
        'ecwid_priority',
        AjaxSelectFieldWidget,
        source=possibleProducts,
        pattern_options={'allowNewItems': False},
    )


class SettingsEditForm(RegistryEditForm):
    schema = ISettings
    schema_prefix = 'somesite.website'
    label = _(u'somesite_settings_title', default=u'Somesite Settings')


SettingsView = layout.wrap_form(SettingsEditForm, ControlPanelFormWrapper)

I've tried following the advice here:

However that doesn't seem to work...

How can I avoid this error, I only want to have a dynamic Ajax vocabulary in my configlet schema, but it seems to be giving me a lot of headaches...

Thanks for the help!

1 Like

I have read in the Plone 5 documentation that in the registry, you can only use a limited set of fields. Your error information reports about a problem with the definition of ecwid_priority; apparently, the List used there is unacceptable in the registry. You might read the documentation to find out whether there is a similar acceptable field (maybe Tuple).

If you can not use a tuple, you could maybe use DataGridField

If you do, remember to

  1. Install the add-on
  2. Use these imports

I'm still not understanding this, how can I use a dynamic vocabulary with a list + choice field in the Plone registry. From what I can see from the example above, you have to define the specific fields...

Better yet, is there a way to inherit some Content-Type behaviour in my configlet so that I can use the default schema functionality..?

I am a bit busy at the moment, but is this what you want to do:

  1. Have a control panel where you can set some settings, where one is a choice field with a dynamic vocabulary

  2. Use this setting for other stuff in the site?

Maybe you can do it like this:

If not, the bda.plone.shop add-ons have a lot of 'complicated settings', I am pretty sure there is something there you can use:

Just solved this. The error message is a bit misleading. You can use schema.List as a registry field - but the vocabulary must be a named vocabulary.

plone.registry/plone/registry/registry.rst at master · plone/plone.registry · GitHub

  • For Choice fields, only named vocabularies are supported: you can not reference a particular source or source binder

Your error is here - possibleProducts is not a named vocabulary.

        value_type=schema.Choice(source=possibleProducts),

So, Read:
https://docs.plone.org/develop/plone/forms/vocabularies.html#registering-a-named-vocabulary-provider-in-zcml

Change:

    value_type = schema.Choice(source="possibleProducts"),

And some zcml:

<utility name="possibleProducts"
         provides="zope.schema.interfaces.IVocabularyFactory"
         factory="dotted.path.to.code.possibleProducts"
 />          

and it should work.

Take note that the docs use zcml:component attribute and I'm using zcml:factory attribute. I think 'factory' is the correct attribute for you and all dynamic vocabularies. A component, in this case, would be an actual IVocabulary implementation (like SHOP_STATUSES). A factory is called at runtime and will build you an IVocabulary and return it.

Sorry for the late reply. Hope it helps you and future googlers.

1 Like