1+ '''
2+ Created on Mar 26, 2014
3+
4+ @author: LAB_ADM
5+ '''
6+ from django .utils .translation import ugettext_lazy as _
7+ from xadmin .filters import manager ,MultiSelectFieldListFilter
8+ from xadmin .plugins .filters import *
9+
10+ @manager .register
11+ class QuickFilterMultiSelectFieldListFilter (MultiSelectFieldListFilter ):
12+ """ Delegates the filter to the default filter and ors the results of each
13+
14+ Lists the distinct values of each field as a checkbox
15+ Uses the default spec for each
16+
17+ """
18+ template = 'xadmin/filters/quickfilter.html'
19+
20+ class QuickFilterPlugin (BaseAdminPlugin ):
21+ """ Add a filter menu to the left column of the page """
22+ list_quick_filter = () # these must be a subset of list_filter to work
23+ quickfilter = {}
24+ search_fields = ()
25+ free_query_filter = True
26+
27+ def init_request (self , * args , ** kwargs ):
28+ menu_style_accordian = hasattr (self .admin_view ,'menu_style' ) and self .admin_view .menu_style == 'accordion'
29+ return bool (self .list_quick_filter ) and not menu_style_accordian
30+
31+ # Media
32+ def get_media (self , media ):
33+ return media + self .vendor ('xadmin.plugin.quickfilter.js' ,'xadmin.plugin.quickfilter.css' )
34+
35+ def lookup_allowed (self , lookup , value ):
36+ model = self .model
37+ # Check FKey lookups that are allowed, so that popups produced by
38+ # ForeignKeyRawIdWidget, on the basis of ForeignKey.limit_choices_to,
39+ # are allowed to work.
40+ for l in model ._meta .related_fkey_lookups :
41+ for k , v in widgets .url_params_from_lookup_dict (l ).items ():
42+ if k == lookup and v == value :
43+ return True
44+
45+ parts = lookup .split (LOOKUP_SEP )
46+
47+ # Last term in lookup is a query term (__exact, __startswith etc)
48+ # This term can be ignored.
49+ if len (parts ) > 1 and parts [- 1 ] in QUERY_TERMS :
50+ parts .pop ()
51+
52+ # Special case -- foo__id__exact and foo__id queries are implied
53+ # if foo has been specificially included in the lookup list; so
54+ # drop __id if it is the last part. However, first we need to find
55+ # the pk attribute name.
56+ rel_name = None
57+ for part in parts [:- 1 ]:
58+ try :
59+ field , _ , _ , _ = model ._meta .get_field_by_name (part )
60+ except FieldDoesNotExist :
61+ # Lookups on non-existants fields are ok, since they're ignored
62+ # later.
63+ return True
64+ if hasattr (field , 'rel' ):
65+ model = field .rel .to
66+ rel_name = field .rel .get_related_field ().name
67+ elif isinstance (field , RelatedObject ):
68+ model = field .model
69+ rel_name = model ._meta .pk .name
70+ else :
71+ rel_name = None
72+ if rel_name and len (parts ) > 1 and parts [- 1 ] == rel_name :
73+ parts .pop ()
74+
75+ if len (parts ) == 1 :
76+ return True
77+ clean_lookup = LOOKUP_SEP .join (parts )
78+ return clean_lookup in self .list_quick_filter
79+
80+ def get_list_queryset (self , queryset ):
81+ lookup_params = dict ([(smart_str (k )[len (FILTER_PREFIX ):], v ) for k , v in self .admin_view .params .items () if smart_str (k ).startswith (FILTER_PREFIX ) and v != '' ])
82+ for p_key , p_val in lookup_params .iteritems ():
83+ if p_val == "False" :
84+ lookup_params [p_key ] = False
85+ use_distinct = False
86+
87+ if not hasattr (self .admin_view ,'quickfilter' ):
88+ self .admin_view .quickfilter = {}
89+
90+ # for clean filters
91+ self .admin_view .quickfilter ['has_query_param' ] = bool (lookup_params )
92+ self .admin_view .quickfilter ['clean_query_url' ] = self .admin_view .get_query_string (remove = [k for k in self .request .GET .keys () if k .startswith (FILTER_PREFIX )])
93+
94+ # Normalize the types of keys
95+ if not self .free_query_filter :
96+ for key , value in lookup_params .items ():
97+ if not self .lookup_allowed (key , value ):
98+ raise SuspiciousOperation ("Filtering by %s not allowed" % key )
99+
100+ self .filter_specs = []
101+ if self .list_quick_filter :
102+ for list_quick_filter in self .list_quick_filter :
103+ field_path = None
104+ field_order_by = None
105+ field_limit = None
106+ field_parts = []
107+ sort_key = None
108+ cache_config = None
109+
110+ if type (list_quick_filter )== dict and 'field' in list_quick_filter :
111+ field = list_quick_filter ['field' ]
112+ if 'order_by' in list_quick_filter :
113+ field_order_by = list_quick_filter ['order_by' ]
114+ if 'limit' in list_quick_filter :
115+ field_limit = list_quick_filter ['limit' ]
116+ if 'sort' in list_quick_filter and callable (list_quick_filter ['sort' ]):
117+ sort_key = list_quick_filter ['sort' ]
118+ if 'cache' in list_quick_filter and type (list_quick_filter )== dict :
119+ cache_config = list_quick_filter ['cache' ]
120+
121+ else :
122+ field = list_quick_filter # This plugin only uses MultiselectFieldListFilter
123+
124+ if not isinstance (field , models .Field ):
125+ field_path = field
126+ field_parts = get_fields_from_path (self .model , field_path )
127+ field = field_parts [- 1 ]
128+ spec = QuickFilterMultiSelectFieldListFilter (field , self .request , lookup_params ,self .model , self .admin_view , field_path = field_path ,field_order_by = field_order_by ,field_limit = field_limit ,sort_key = sort_key ,cache_config = cache_config )
129+
130+ if len (field_parts )> 1 :
131+ spec .title = "%s %s" % (field_parts [- 2 ].name ,spec .title )
132+
133+ # Check if we need to use distinct()
134+ use_distinct = True #(use_distinct orlookup_needs_distinct(self.opts, field_path))
135+ if spec and spec .has_output ():
136+ try :
137+ new_qs = spec .do_filte (queryset )
138+ except ValidationError , e :
139+ new_qs = None
140+ self .admin_view .message_user (_ ("<b>Filtering error:</b> %s" ) % e .messages [0 ], 'error' )
141+ if new_qs is not None :
142+ queryset = new_qs
143+
144+ self .filter_specs .append (spec )
145+
146+ self .has_filters = bool (self .filter_specs )
147+ self .admin_view .quickfilter ['filter_specs' ] = self .filter_specs
148+ self .admin_view .quickfilter ['used_filter_num' ] = len (filter (lambda f : f .is_used , self .filter_specs ))
149+
150+ if use_distinct :
151+ return queryset .distinct ()
152+ else :
153+ return queryset
154+
155+ def block_left_navbar (self , context , nodes ):
156+ nodes .append (loader .render_to_string ('xadmin/blocks/modal_list.left_navbar.quickfilter.html' ,context ))
157+
158+ site .register_plugin (QuickFilterPlugin , ListAdminView )
0 commit comments