Skip to content

Commit 6c9285d

Browse files
authored
Merge branch 'master' into master
2 parents 54ece3d + d931e77 commit 6c9285d

File tree

7 files changed

+301
-129
lines changed

7 files changed

+301
-129
lines changed

sortedm2m/admin.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from django import forms
2+
from django.conf import settings
3+
from django.utils import translation
4+
from django.contrib.admin.widgets import AutocompleteSelectMultiple
5+
6+
class OrderedAutocomplete(AutocompleteSelectMultiple):
7+
def optgroups(self, name, value, attr=None):
8+
"""Return selected options based on the ModelChoiceIterator."""
9+
default = (None, [], 0)
10+
groups = [default]
11+
has_selected = False
12+
# Use a list instead of a set to keep around the order returned
13+
# by SortedManyToManyField
14+
selected_choices = [
15+
str(v) for v in value
16+
if str(v) not in self.choices.field.empty_values
17+
]
18+
if not self.is_required and not self.allow_multiple_selected:
19+
default[1].append(self.create_option(name, "", "", False, 0))
20+
remote_model_opts = self.field.remote_field.model._meta
21+
to_field_name = getattr(
22+
self.field.remote_field, "field_name", remote_model_opts.pk.attname
23+
)
24+
to_field_name = remote_model_opts.get_field(to_field_name).attname
25+
choices = (
26+
(getattr(obj, to_field_name), self.choices.field.label_from_instance(obj))
27+
for obj in self.choices.queryset.using(self.db).filter(
28+
**{"%s__in" % to_field_name: selected_choices}
29+
)
30+
)
31+
# Sort choices according to what is returned by SortedManyToManyField
32+
choices = list(choices)
33+
choices.sort(key=lambda x: selected_choices.index(str(x[0])))
34+
for option_value, option_label in choices:
35+
selected = str(option_value) in value and (
36+
has_selected is False or self.allow_multiple_selected
37+
)
38+
has_selected |= selected
39+
index = len(default[1])
40+
subgroup = default[1]
41+
subgroup.append(
42+
self.create_option(
43+
name, option_value, option_label, selected_choices, index
44+
)
45+
)
46+
return groups
47+
48+
class Media:
49+
extra = "" if settings.DEBUG else ".min"
50+
lang = translation.get_language()
51+
js = (
52+
"admin/js/vendor/jquery/jquery%s.js" % extra,
53+
"admin/js/vendor/select2/select2.full%s.js" % extra,
54+
) + (
55+
"admin/js/vendor/select2/i18n/%s.js" % lang,
56+
) + (
57+
'sortedm2m/jquery-ui.min.js',
58+
"admin/js/jquery.init.js",
59+
"sortedm2m/ordered_autocomplete.js"
60+
)
61+
css = {
62+
"screen": (
63+
"admin/css/vendor/select2/select2%s.css" % extra,
64+
"admin/css/autocomplete.css",
65+
"sortedm2m/ordered_autocomplete.css",
66+
)
67+
}
68+
69+
70+
class SortedM2MAutocompleteMixin:
71+
72+
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
73+
using = kwargs.get("using")
74+
if db_field.name in self.sorted_autocomplete_fields:
75+
kwargs['widget'] = OrderedAutocomplete(
76+
db_field,
77+
self.admin_site,
78+
using=using
79+
)
80+
if 'queryset' not in kwargs:
81+
queryset = self.get_field_queryset(using, db_field, request)
82+
if queryset is not None:
83+
kwargs['queryset'] = queryset
84+
85+
form_field = db_field.formfield(**kwargs)
86+
return form_field
87+
88+
return super().formfield_for_manytomany(db_field, request, **kwargs)

0 commit comments

Comments
 (0)