PageRenderTime 55ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/ella/newman/filterspecs.py

https://github.com/dedaluz/ella
Python | 247 lines | 209 code | 15 blank | 23 comment | 18 complexity | 8536064f0353abf39114297405f1169d MD5 | raw file
Possible License(s): BSD-3-Clause
  1. """
  2. Customized FilterSpecs.
  3. """
  4. import logging
  5. from django.utils.translation import ugettext as _
  6. from django.contrib.admin import filterspecs
  7. log = logging.getLogger('ella.newman')
  8. class CustomFilterSpec(filterspecs.FilterSpec):
  9. """ custom defined FilterSpec """
  10. def __init__(self, f, request, params, model, model_admin, field_path=None):
  11. self.state = 0
  12. self.params = params
  13. self.model = model
  14. self.links = []
  15. self.model_admin = model_admin
  16. self.field_path = field_path
  17. self.user = request.user
  18. #self.lookup_val = request.GET.get(self.lookup_kwarg, None) #selected filter value (not label)
  19. self.lookup_kwarg = 'NOT SET'
  20. self.f = f
  21. self.request_get = request.GET
  22. super(CustomFilterSpec, self).__init__(f, request, params, model, model_admin, field_path=field_path)
  23. self.request_path_info = request.path_info
  24. self.title_text = self.field.verbose_name
  25. self.active_filter_lookup = None
  26. self.all_choices = []
  27. self.selected_item = None
  28. def filter_func(self):
  29. raise NotImplementedError('filter_func() method should be overloaded (substituted at run-time).')
  30. def title(self):
  31. return self.title_text
  32. def get_lookup_kwarg(self):
  33. """
  34. this method can be specified as second argument in @filter_spec decorator. (see below)
  35. If more than one GET parameter is used to filter queryset,
  36. get_lookup_kwarg() should return list containing these parameters
  37. (suitable esp. for calendar/date filters etc.).
  38. """
  39. return self.lookup_kwarg
  40. def get_active(self, request_params):
  41. if self.active_filter_lookup is not None: # cached result
  42. return self.active_filter_lookup
  43. self.active_filter_lookup = []
  44. lookup_multi = 0
  45. lookup = self.get_lookup_kwarg()
  46. if type(lookup) == list:
  47. lookup_multi = len(lookup)
  48. found = 0
  49. for p in request_params:
  50. if not lookup_multi and p == lookup:
  51. self.active_filter_lookup = [lookup]
  52. break
  53. elif lookup_multi:
  54. if p in lookup:
  55. found += 1
  56. if found == lookup_multi:
  57. self.active_filter_lookup = lookup
  58. break
  59. return self.active_filter_lookup
  60. def filter_active(self):
  61. " Can be used from template. "
  62. return self.is_active(self.request_get)
  63. def is_active(self, request_params):
  64. """
  65. Returns True if filter is applied, otherwise returns False.
  66. Tries to find its argument(s) in request querystring.
  67. """
  68. return len(self.get_active(request_params)) > 0
  69. def is_selected_item(self):
  70. """
  71. Returns empty dict if no filter item is selected.
  72. Otherwise returns dict containing GET params as keys and corresponding
  73. values.
  74. """
  75. active = self.get_active(self.request_get)
  76. out = dict()
  77. for par in active:
  78. if par in self.request_get:
  79. out[par] = self.request_get[par]
  80. return out
  81. def get_disabled_params(self):
  82. " Returns parameter dict for constructing HREF to disable this filter. "
  83. out = dict()
  84. for key in self.request_get:
  85. if key in self.get_active(self.request_get):
  86. continue
  87. out[key] = self.request_get[key]
  88. return out
  89. def generate_choices(self, cl):
  90. def make_unicode_params(pdict):
  91. " param values converted to unicode are needed to make dict to dict parameter comparison. "
  92. out = dict()
  93. for key in pdict:
  94. out[key] = unicode(pdict[key])
  95. return out
  96. if self.filter_func():
  97. self.state = 1
  98. if self.state <= 0:
  99. yield dict()
  100. lookup = self.get_lookup_kwarg()
  101. selected = self.is_selected_item()
  102. # Reset filter button/a href
  103. yield {'selected': len(selected.keys()) == 0,
  104. 'query_string': cl.get_query_string(None, self.get_active(self.request_get) ),
  105. 'display': _('All')}
  106. for title, param_dict in self.links:
  107. params = make_unicode_params(param_dict)
  108. yield {'selected': selected == params,
  109. 'query_string': cl.get_query_string(param_dict, []),
  110. 'display': title}
  111. def get_selected(self):
  112. " Should be used within a template to get selected item in filter. "
  113. if self.selected_item:
  114. return self.selected_item
  115. if not self.all_choices:
  116. # return the same structure with error key set
  117. return {
  118. 'selected': False,
  119. 'query_string':'',
  120. 'display': '',
  121. 'error': 'TOO EARLY'
  122. }
  123. for item in self.all_choices:
  124. if item['selected']:
  125. self.selected_item = item
  126. return item
  127. def choices(self, cl):
  128. if not self.all_choices:
  129. self.all_choices = map(None, self.generate_choices(cl))
  130. return self.all_choices
  131. def filterspec_preregister(cls, test, factory):
  132. """ method inserts FilterSpec and test to the beginning of FilterSpec registration table """
  133. cls.filter_specs.insert(0, (test, factory))
  134. def filterspec_clean_all(cls):
  135. while cls.filter_specs:
  136. cls.filter_specs.pop()
  137. # Adding class method register_insert() to FilterSpec.
  138. # important is to run following code before admin.py
  139. filterspecs.FilterSpec.register_insert = classmethod(filterspec_preregister)
  140. filterspecs.FilterSpec.clean_registrations = classmethod(filterspec_clean_all)
  141. def filter_spec(field_test_func, lookup_kwarg_func=None, title=None):
  142. """
  143. Decorator ``filter_spec`` creates custom filter.
  144. Example:
  145. @filter_spec(lambda field_to_test: isinstance(field_to_test, models.DateField))
  146. @filter_spec(lambda field_to_test: isinstance(field_to_test, models.DateField), lambda p: 'category__exact')
  147. """
  148. def decorate(filter_func):
  149. name = '%s_%s' % (filter_func.__name__, CustomFilterSpec.__name__)
  150. cls = type(name, (CustomFilterSpec,), {})
  151. cls.filter_func = filter_func
  152. if lookup_kwarg_func:
  153. cls.get_lookup_kwarg = lookup_kwarg_func
  154. if title:
  155. cls.title = lambda fspec: title
  156. filterspecs.FilterSpec.register_insert(field_test_func, cls)
  157. return filter_func
  158. return decorate
  159. # -------------------------------------
  160. # Standard django.admin filters
  161. # -------------------------------------
  162. # TODO make common parent of FilterSpecEnhancement and CustomFilterSpec
  163. class FilterSpecEnhancement(filterspecs.FilterSpec):
  164. def filter_active(self):
  165. " Can be used from template. "
  166. return self.is_active(self.params)
  167. def get_selected(self):
  168. " Should be used within a template to get selected item in filter. "
  169. if hasattr(self, 'selected_item'):
  170. return self.selected_item
  171. if not hasattr(self, 'all_choices'):
  172. # return the same structure with error key set
  173. return {
  174. 'selected': False,
  175. 'query_string':'',
  176. 'display': '',
  177. 'error': 'TOO EARLY'
  178. }
  179. for item in self.all_choices:
  180. if item['selected']:
  181. self.selected_item = item
  182. return item
  183. class RelatedFilterSpec(filterspecs.RelatedFilterSpec, FilterSpecEnhancement):
  184. def is_active(self, request_params):
  185. """
  186. Returns True if filter is applied, otherwise returns False.
  187. Tries to find its argument(s) in request querystring.
  188. """
  189. return self.lookup_kwarg in request_params
  190. def choices(self, cl):
  191. if not hasattr(self, 'all_choices'):
  192. c = super(self.__class__, self).choices(cl)
  193. self.all_choices = map(None, c)
  194. return self.all_choices
  195. filterspecs.FilterSpec.register_insert(lambda f: bool(f.rel), RelatedFilterSpec)
  196. class ChoicesFilterSpec(filterspecs.ChoicesFilterSpec, FilterSpecEnhancement):
  197. def is_active(self, request_params):
  198. return self.lookup_kwarg in request_params
  199. filterspecs.FilterSpec.register_insert(lambda f: bool(f.choices), ChoicesFilterSpec)
  200. class DateFieldFilterSpec(filterspecs.DateFieldFilterSpec, FilterSpecEnhancement):
  201. def is_active(self, request_params):
  202. return False
  203. filterspecs.FilterSpec.register_insert(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec)
  204. class BooleanFieldFilterSpec(filterspecs.BooleanFieldFilterSpec, FilterSpecEnhancement):
  205. def is_active(self, request_params):
  206. return False
  207. filterspecs.FilterSpec.register_insert(lambda f: isinstance(f, models.BooleanField), BooleanFieldFilterSpec)
  208. from django.db import models
  209. filterspecs.FilterSpec.register_insert(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec)