Dexterity Choice Field (first element be considered a non-value)

I have a required Dexterity choice field with a list of possible values:

Please choose one:
Choice A
Choice B
Choice C

I would like the first element that says, "Please choose one:" as a choice with no value so that it forces the user to choose either Choice A, B or C. How can I make the first element a non-value?

I have this in my schema model:

  <field name="choices" type="zope.schema.Choice" indexer:searchable="true">
    <description/>
    <required>True</required>
    <title>Choices</title>
    <values>
      <element>Please choose one:</element>
      <element>Choice A</element>
      <element>Choice B</element>
      <element>Choice C</element>
    </values>
  </field>

Any advice would be most appreciated. Thanks so much

You need to force the widget to show the "prompt" even though it's a required field, and customize the prompt message:

<field name="choices" type="zope.schema.Choice" indexer:searchable="true">
    <description/>
    <required>True</required>
    <title>Choices</title>
    <values>
        <element>Choice A</element>
        <element>Choice B</element>
        <element>Choice C</element>
    </values>
    <form:widget>
        <prompt>True</prompt>
        <promptMessage>Please choose one:</promptMessage>
    </form:widget>
</field>

Also make sure you have declared the form XML namespace in the file's top-level tag if it's not there already: xmlns:form="http://namespaces.plone.org/supermodel/form"

1 Like

Thank you. Yes, I do have the form declaration at the top.

Doing what you have suggested, the list now shows Choice A by default. Adding a blank choice below does not help either because Plone sees it as a valid entry and not as a non-value entry:

<field name="choices" type="zope.schema.Choice" indexer:searchable="true">
<description/>
<required>True</required>
<title>Choices</title>
<values>
    <element></element>
    <element>Choice A</element>
    <element>Choice B</element>
    <element>Choice C</element>
</values>
<form:widget>
    <prompt>True</prompt>
    <promptMessage>Please choose one:</promptMessage>
</form:widget>

Please advise.

There is another option (but I am not sure if that is 'best practice":

  • Make the first choice "Please Choose"
  • Make a validator so "Please Choose" is not permitted.

I am sure this has been discussed here before, but I could not find the discussion

Are you referring to this?

If so, based on it, this is what I did:
Added the following in file called policy.py in my.product/src/my/product/policy.py

"""Module where all interfaces, events and exceptions live."""

from ncdhhscontenttype.ncssinfo import _
from zope import schema
from zope.interface import Interface
from zope.publisher.interfaces.browser import IDefaultBrowserLayer
from plone.supermodel import model
from Acquisition import aq_inner
from plone import api
from Products.Five import BrowserView
from z3c.form import validator
from zope.interface import Invalid


class IMyProductLayer(IDefaultBrowserLayer):
    """Marker interface that defines a browser layer."""

class CustomValidator(validator.SimpleFieldValidator):
    def validate(self, value):
        super(CustomValidator, self).validate(value)
        if value:
            if ['Please choose one:']:
                raise Invalid(u'Value is not valid')

In my schema model file, policy.xml file, I called it using the following:

<field name="choices" type="zope.schema.Choice" indexer:searchable="true">
    <description/>
    <required>True</required>
    <title>Choices</title>
    <values>
        <element>Please choose one:</element>
        <element>Choice A</element>
        <element>Choice B</element>
        <element>Choice C</element>
    </values>
    <form:validator="my.product.policy.CustomValidator">
</field>

Error message:

SupermodelParseError: error parsing attribute name

It is referring to the line that has the form:validator statement in my policy.xml file. I am sure I am missing something. Any help would be most appreciated. Thank you.

Change both "Please choose one:" to "Choose" and I think it will work.

(to use your wording, I can't answer right now (it is weekend… ))

Tried that and it still did not work. It gave me the same error as before. Have a great weekend! :slight_smile:

Sounds like it is ignoring the form:widget section. Support for that was added in plone.autoform 1.4; maybe you're using an older version?

while I'm not quite answering to the original question, I'd just say that it should be possible with file based Python code since it can be done TTW with Ambidexterity

Validator

if value not in ("1","2","3"):
    error_message = "pick a value"

Vocabulary

vocabulary = [("Pick a value",""),("title for value 1","1"),("title for value 2","2"),("title for value 3","3")]

If you don't have to do it TTW, and you can define your vocabulary in your add-on, this is what works for us:

@implementer(IVocabularyFactory)
class YesNoAllVocabulary(object):

    def __call__(self, context):
        items = []
        items.append(SimpleTerm('1', '1', _(u'Yes')))
        items.append(SimpleTerm('0', '0', _(u'No')))
        items.append(SimpleTerm('--NOVALUE--', '--NOVALUE--', _(u'All')))
        return SimpleVocabulary(items)

YesNoAllVocabularyFactory = YesNoAllVocabulary()

You need to explicitly set the --NOVALUE-- token, but I couldn't find a way to do this TTW with the Dexterity Schema Editor.

I tested your example in Plone 5.1 with plone.autoform = 1.7.5 and it does not work.

This is my Plone version:

Plone 5.0.8 (5018)
CMF 2.2.10
Zope 2.13.26
Python 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
PIL 3.4.2 (Pillow)

Is it working for you in Plone 5.0.8?

Thank you so much for the codes. So in my case, could I just define NOVALUE such as:
@implementer(IVocabularyFactory)
class YesNoAllVocabulary(object):

def __call__(self, context):
    items = []
    items.append(SimpleTerm('Choose', '--NOVALUE--', _(u'All')))
    return SimpleVocabulary(items)

YesNoAllVocabularyFactory = YesNoAllVocabulary()

How do I then call this?
form:validator="ncdhhscontenttype.ncssinfo.ncssi_policy.YesNoAllVocabulary"

This is just a code convention suggestion, instead of using '--NOVALUE-- "out of nowhere" you can do from z3c.form.interfaces import NOVALUE.

Thanks idgserpro, I will try it out.