with form fields errors creating django django-forms cleaned-data

form - Django MultipleChoiceField no conserva el orden de los valores seleccionados



form errors django (3)

Tengo un Django ModelForm que expone un campo de opción múltiple correspondiente a una relación de muchos a muchos a través de un modelo que mantiene el order de selección (una lista de documentos) como un atributo adicional. En el front-end, el campo se muestra como dos campos de selección múltiple similares a los de administración, uno para enumerar las opciones disponibles y el otro contiene los elementos seleccionados.

El formulario se puede guardar con la selección correcta de elementos, pero siempre están en el orden del orden de elección original, no en la selección. El navegador envía la selección en el orden correcto, pero el orden en form.cleaned_data[''documents''] es siempre el orden en el orden de elección original.

¿Cómo puedo hacer que MultipleChoiceField respete el orden de los elementos seleccionados?

Gracias.


No hay una manera simple. O bien debe anular el método de clean de MultipleChoiceField o, como mencionó en su comentario, usar la lista de obtención para reordenarlos manualmente. Es probable que dependa de la frecuencia con la que debe hacerlo en su código.

El método clean de MultipleChoiceField crea un QuerySet que está recibiendo, al filtrar una lista de objetos a través del operador IN esta manera, por lo que la base de datos proporciona la orden:

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

Puede heredar de 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)))

El inconveniente es que el valor devuelto ya no es un QuerySet sino una lista ordinaria.


Para devolver un QuerySet ordenado al anular el método de limpieza, también puede hacer esto:

class OrderedModelMultipleChoiceField(ModelMultipleChoiceField): def clean(self, value): qs = super(OrderedModelMultipleChoiceField, self).clean(value) clauses = '' ''.join([''WHEN id=%s THEN %s'' % (pk, i) for i, pk in enumerate(value)]) return qs.filter(pk__in=value).extra( select={''ordering'': ''CASE %s END'' % clauses}, order_by=(''ordering'',) )


Lo hice a través de un Widget. El beneficio de esto es que se ordenará correctamente en diferentes idiomas:

class SortedSelectMultiple(SelectMultiple): def render_options(self, selected_choices): self.choices = sorted(self.choices) self.choices.sort(key=lambda x: x[1]) return super(SortedSelectMultiple, self).render_options(selected_choices)