Django MultipleChoiceField does not preserve order of selected values

cleaned-data, django, django-forms

Solution

There is no simple way. You either need to override the `clean` method of the `MultipleChoiceField` or, as you mentioned in your comment, use the getlist to re-order them manually. It probably depends how often in your code do you need to do it.

The `clean` method of `MultipleChoiceField` creates a `QuerySet` that you are receiving, by filtering an object list through the `IN` operator like this, so the order is given by the database:

qs = self.queryset.filter(**{'%s__in' % key: value})

You can inherit from `ModelMultipleChoiceField`:

class OrderedModelMultipleChoiceField(ModelMultipleChoiceField):
    def clean(self, value):
        qs = super(OrderedModelMultipleChoiceField, self).clean(value)
        return sorted(qs, lambda a,b: sorted(qs, key=lambda x:value.index(x.pk)))

The drawback is that the returned value is no longer a `QuerySet` but an ordinary list.

Problem

I have a Django ModelForm which exposes a multiple choice field corresponding to a many-to-many relation through a model which holds `order` of selection (a list of documents) as an extra attribute. At the front-end, the field is displayed as two multiple select fields similar to that in admin, one to list available choices and the other holds the selected elements. The form can be saved with the correct selection of elements but they are always in the order of the original order of choices, not the selection. The browser sends the selection in correct order, but order in `form.cleaned_data['documents']` is always the order in original order of choices. How can I make the MultipleChoiceField respect the order of elements selected? Thanks.

Original source

Related problems