Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drag'n'drop sorting with select2 autocomplete widget #171

Open
amirouche opened this issue Aug 28, 2020 · 0 comments
Open

Drag'n'drop sorting with select2 autocomplete widget #171

amirouche opened this issue Aug 28, 2020 · 0 comments

Comments

@amirouche
Copy link
Contributor

We added the ability to sort using drag'n'drop and select2 autocomplete that is more user friendly that the current filtering approach. We would like to contribute that feature to this awesome project.

Here is the code we use at the moment:

from django.contrib.admin.widgets import AutocompleteSelectMultiple


class OrderedAutocomplete(AutocompleteSelectMultiple):

    def optgroups(self, name, value, attr=None):
        """Return selected options based on the ModelChoiceIterator."""
        # XXX: This is based on django.contrib.admin.widgets.AutocompleteMixin:
        default = (None, [], 0)
        groups = [default]
        has_selected = False
        # Use a list instead of a set to keep around the order returned
        # by SortedManyToManyField
        selected_choices = [
            str(v) for v in value
            if str(v) not in self.choices.field.empty_values
        ]
        if not self.is_required and not self.allow_multiple_selected:
            default[1].append(self.create_option(name, '', '', False, 0))
        choices = (
            (obj.pk, self.choices.field.label_from_instance(obj))
            for obj in self.choices.queryset.using(self.db).filter(pk__in=selected_choices)
        )
        choices = list(choices)
        # Sort choices according to what is returned by SortedManyToManyField
        choices.sort(key=lambda x: selected_choices.index(str(x[0])))
        for option_value, option_label in choices:
            selected = (
                str(option_value) in value and
                (has_selected is False or self.allow_multiple_selected)
            )
            has_selected |= selected
            index = len(default[1])
            subgroup = default[1]
            subgroup.append(self.create_option(name, option_value, option_label, selected_choices, index))
        return groups


class OrderedAutocompleteMixin:

    def formfield_for_manytomany(self, db_field, request=None, **kwargs):
        using = kwargs.get("using")
        if db_field.name in self.ordered_autocomplete_fields:
            kwargs['widget'] = OrderedAutocomplete(
                db_field.remote_field,
                self.admin_site,
                using=using
            )
            if 'queryset' not in kwargs:
                queryset = self.get_field_queryset(using, db_field, request)
                if queryset is not None:
                    kwargs['queryset'] = queryset

            form_field = db_field.formfield(**kwargs)
            return form_field

        return super().formfield_for_manytomany(db_field, request, **kwargs)
         (function($) {
             $(document).ready(function() {
                 // Add drag'n'drop to django select2 autocomplete fields.
                 // https://github.com/select2/select2/issues/3004#issuecomment-485821449
                 let selects = $('select.admin-autocomplete')
                 selects.each(function(index, element) {
                     let select = $(element).select2();
                     let children = select.next().children().children().children();
                     children.sortable({
                         containment: 'parent',
                         stop: function (event, ui) {
                             ui.item.parent().children('[title]').each(function () {
                                 let title = $(this).attr('title');
                                 let original = $('option:contains(' + title + ')', select).first();
                                 original.detach();
                                 select.append(original)
                             });
                             select.change();
                         }
                     });
                 });
             });
         })(grp.jQuery);

ref: select2/select2#3004 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant