@@ -20,13 +20,12 @@ def __init__(self, field, request, params, model, model_admin, field_path):
2020 # Obey parent ModelAdmin queryset when deciding which options to show
2121 if model == parent_model :
2222 queryset = model_admin .get_queryset (request )
23- self .lookup_choices = (
24- queryset .distinct ().order_by (field .name ).values_list (field .name , flat = True )
25- )
2623 else :
2724 queryset = parent_model ._default_manager .all ()
28- self .lookup_choices = queryset .distinct ()
29-
25+ self .lookup_choices = (
26+ queryset .distinct ().order_by (field .name ).values_list (field .name , flat = True )
27+ )
28+
3029 def expected_parameters (self ):
3130 return [self .lookup_kwarg , self .lookup_kwarg_isnull ]
3231
@@ -76,3 +75,51 @@ def choices(self, changelist):
7675 ),
7776 "display" : self .empty_value_display ,
7877 }
78+
79+
80+ class MultiSelectRelatedFieldListFilter (admin .RelatedFieldListFilter ):
81+ def __init__ (self , field , request , params , model , model_admin , field_path ):
82+ super ().__init__ (field , request , params , model , model_admin , field_path )
83+ self .lookup_kwarg = "%s__%s__in" % (field_path , field .target_field .name )
84+ self .lookup_kwarg_isnull = "%s__isnull" % field_path
85+ values = params .get (self .lookup_kwarg , [])
86+ self .lookup_val = values .split ("," ) if values else []
87+ self .lookup_choices = self .field_choices (field , request , model_admin )
88+
89+ def choices (self , changelist ):
90+ yield {
91+ "selected" : self .lookup_val is None and not self .lookup_val_isnull ,
92+ "query_string" : changelist .get_query_string (
93+ remove = [self .lookup_kwarg , self .lookup_kwarg_isnull ]
94+ ),
95+ "display" : _ ("All" ),
96+ }
97+
98+ for pk_val , val in self .lookup_choices :
99+ if val is None :
100+ self .include_empty_choice = True
101+ continue
102+ val = str (val )
103+
104+ if str (pk_val ) in self .lookup_val :
105+ values = [str (v ) for v in self .lookup_val if str (v ) != str (pk_val )]
106+ else :
107+ values = self .lookup_val + [str (pk_val )]
108+
109+ yield {
110+ "selected" : self .lookup_val is not None
111+ and str (pk_val ) in self .lookup_val ,
112+ "query_string" : changelist .get_query_string (
113+ {self .lookup_kwarg : "," .join (values )}, [self .lookup_kwarg_isnull ]
114+ ),
115+ "display" : val ,
116+ }
117+ empty_title = self .empty_value_display
118+ if self .include_empty_choice :
119+ yield {
120+ "selected" : bool (self .lookup_val_isnull ),
121+ "query_string" : changelist .get_query_string (
122+ {self .lookup_kwarg_isnull : "True" }, [self .lookup_kwarg ]
123+ ),
124+ "display" : empty_title ,
125+ }
0 commit comments