SelectFieldWidget breaks sort order of lists

SelectFieldWidget uses vocabulary order to display selected items in the edit form (not the field's order). This leads to order being changed accidentally on edit.

To Reproduce

Given: A SelectFieldWidget on a RelationList with a Vocabulary that is sorted by Title

from plone.app.z3cform.widget import SelectFieldWidget

    directives.widget("persons", SelectFieldWidget)
    persons = RelationList(
        title=_(u"Persons"),
        description=_("Choose persons that shall be listed for this location."),
        required=False,
        default=[],
        value_type=RelationChoice(vocabulary="my.package.location_persons"),
    )

Select (and/or) order selected items: PersonC, PersonB, PersonA

Save

Edit Again

SelectWidget displays: PersonA, PersonB, PersonC

Saving the form (w/o even touching the persons field will change the order of the
selected items.

Possible Solutions

in archetypes i previously worked around this by returning the correct order in the vocabulary (pseudo-code)

def SortedPersonVocabulary(context)
    uids = context.getField('persons').getRaw(context)
    persons = catalog(portal_type="Person", sort_on="sortable_title")
    _max = len(uids)
    def pos(x):
        try:
            return uids.index(x.UID)
        except ValueError:
            return _max

    return to_vocab(sorted(persons, key=pos))

of course i can use the same workaround again but i strongly believe this should be fixed on widget level

first i'd have suggested to change z3c.form.browser.select.SelectWidget.items and make items return the selected items in the correct order prior to the rest of the vocabulary values.

        def pos(term):
            try:
                return self.value.index(term.token)
            except ValueError:
                # keep original pos for all non selected values
                return len(self.value)

        terms = self.terms
        if ISequence.providedBy(self.field) or self.orderable:
            terms = sorted(self.terms, key=pos)

        for idx, term in enumerate(terms):
            addItem(idx, term)

this works, but i'm not sure if a pr like this would be welcomed on zope-level.

browsing through the z3c.form repos i see there is also a OrderedSelectWidget

maybe plone.app.z3cform SelectFieldWidget should use this widget instead the plain selectwidget?

i really wonder why this problem did not bite a lot of plone5 users before and exists at all.

to be honest, i'm a bit confused due to the number the different packages involved.
maybe someone more involved in plone.app.z3cform and mockup can shed some light on this.

side note rant: I also tried AjaxSelectWidget with a searchable vocab person_vocab(context, query=None) or source but ran into the same problems @datar reported in his thread in april 2020. the problem still seems not to be solved.

I think I can anser that one. In core Plone there is almost no need for many2many selections as exposed with the ordered multi select. With a single select there is no ordering issue between the selectied items.

In Plone 4 this relation type was widget-wise taken care of by the inout widget with 2 lists next to each other and you could move items from the inactive to the active list and in the active list reorder them. The only place in core Plone where this widget was used wwas the tinymce control panel to manage the plugins. In Plone 5 it has become a textilnes field.

Another area in Core Plone where this kind of select happens is in the keywords widget/field. But that is also select2 and the ordering there is not relevant either for search results and the keywords are stored in a normal field. They might even be sorted alphabetically in the viewlet below the content? I'm not sure.

Many 2 many 'content' relations as seen in the related items are handled by the different select2 RelatedItems widget and selectable items are sources, not vocabularies. There was some work done to fix/extend support for different sources at the relations sprint early 2021. But it's too easy for me to forget and get confused a bit on the differences/purpose of Sources and Vocabularies and which are now preferred. It's 4 months ago :open_mouth: .

@pbauer @alecpm Am I correct that this post is related to some of the tings we wanted to solve at the relaions sprint? Using "normal' (multi)-select) widgets on RelationChoices for example?

35. Dexterity: Reference — Plone Training 2021 documentation should be/become the reference for what is supported.

thanks for writing this up @fredvd.

this more or less summarizes my findings.

  • RelatedItemsFieldWidget works
  • SelectFieldWidget and AjaxSelectFieldWidget do not support ordering
    (when vocabularies, and - i think - also using sources)
  • AjaxSelectFieldWidget dose not work with searchable vocabulary
  • there is no documentation available how to write a custom source for AjaxSelectFieldWidget and people have unsolved problems in doing so

i also think that at least the training docs for different widgests should state what is supported and also mention limitations.

if there are plans/ideas how to improve this please add a link/summary here @pbauer / @alecpm.
if people know what a solution that has been agreed upon would look like, it is more likely they'll implement it.
thanks for your input

Plone Foundation Code of Conduct