diff --git a/projects/MultiSelectFieldListFilter.py b/projects/MultiSelectFieldListFilter.py new file mode 100644 index 0000000000000000000000000000000000000000..28bbb89e6eae617f147881cdc9e5a5bce7a5a420 --- /dev/null +++ b/projects/MultiSelectFieldListFilter.py @@ -0,0 +1,68 @@ +from django.contrib import admin +from django.contrib.admin.utils import reverse_field_path +from django.utils.translation import gettext_lazy as _ + + +class MultiSelectFieldListFilter(admin.FieldListFilter): + def __init__(self, field, request, params, model, model_admin, field_path): + self.lookup_kwarg = field_path + '__in' + self.lookup_kwarg_isnull = field_path + '__isnull' + + super().__init__(field, request, params, model, model_admin, field_path) + + self.lookup_val = self.used_parameters.get(self.lookup_kwarg, []) + if len(self.lookup_val) == 1 and self.lookup_val[0] == '': + self.lookup_val = [] + self.lookup_val_isnull = self.used_parameters.get( + self.lookup_kwarg_isnull) + + self.empty_value_display = model_admin.get_empty_value_display() + parent_model, reverse_path = reverse_field_path(model, field_path) + # Obey parent ModelAdmin queryset when deciding which options to show + if model == parent_model: + queryset = model_admin.get_queryset(request) + else: + queryset = parent_model._default_manager.all() + self.lookup_choices = queryset.distinct().order_by( + field.name).values_list(field.name, flat=True) + + def expected_parameters(self): + return [self.lookup_kwarg, self.lookup_kwarg_isnull] + + def choices(self, changelist): + yield { + 'selected': not self.lookup_val and self.lookup_val_isnull is None, + 'query_string': changelist.get_query_string(remove=[self.lookup_kwarg, self.lookup_kwarg_isnull]), + 'display': _('All'), + } + include_none = False + for val in self.lookup_choices: + if val is None: + include_none = True + continue + val = str(val) + + if val in self.lookup_val: + values = [v for v in self.lookup_val if v != val] + else: + values = self.lookup_val + [val] + + if values: + yield { + 'selected': val in self.lookup_val, + 'query_string': changelist.get_query_string({self.lookup_kwarg: ','.join(values)}, [self.lookup_kwarg_isnull]), + 'display': val, + } + else: + yield { + 'selected': val in self.lookup_val, + 'query_string': changelist.get_query_string(remove=[self.lookup_kwarg]), + 'display': val, + } + + if include_none: + yield { + 'selected': bool(self.lookup_val_isnull), + 'query_string': changelist.get_query_string({self.lookup_kwarg_isnull: 'True'}, [self.lookup_kwarg]), + 'display': self.empty_value_display, + }