filterspecs.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. """
  2. FilterSpec encapsulates the logic for displaying filters in the Django admin.
  3. Filters are specified in models with the "list_filter" option.
  4. Each filter subclass knows how to display a filter for a field that passes a
  5. certain test -- e.g. being a DateField or ForeignKey.
  6. """
  7. from django.db import models
  8. import datetime
  9. class FilterSpec(object):
  10. filter_specs = []
  11. def __init__(self, f, request, params):
  12. self.field = f
  13. self.params = params
  14. def register(cls, test, factory):
  15. cls.filter_specs.append( (test, factory) )
  16. register = classmethod(register)
  17. def create(cls, f, request, params):
  18. for test, factory in cls.filter_specs:
  19. if test(f):
  20. return factory(f, request, params)
  21. create = classmethod(create)
  22. def has_output(self):
  23. return True
  24. def choices(self, cl):
  25. raise NotImplementedError()
  26. def title(self):
  27. return self.field.verbose_name
  28. def output(self, cl):
  29. t = []
  30. if self.has_output():
  31. t.append(_('<h3>By %s:</h3>\n<ul>\n') % self.title())
  32. for choice in self.choices(cl):
  33. t.append('<li%s><a href="%s">%s</a></li>\n' % \
  34. ((choice['selected'] and ' class="selected"' or ''),
  35. choice['query_string'] ,
  36. choice['display']))
  37. t.append('</ul>\n\n')
  38. return "".join(t)
  39. class RelatedFilterSpec(FilterSpec):
  40. def __init__(self, f, request, params):
  41. super(RelatedFilterSpec, self).__init__(f, request, params)
  42. if isinstance(f, models.ManyToManyField):
  43. self.lookup_title = f.rel.to._meta.verbose_name
  44. else:
  45. self.lookup_title = f.verbose_name
  46. self.lookup_kwarg = '%s__%s__exact' % (f.name, f.rel.to._meta.pk.name)
  47. self.lookup_val = request.GET.get(self.lookup_kwarg, None)
  48. self.lookup_choices = f.rel.to._default_manager.all()
  49. def has_output(self):
  50. return len(self.lookup_choices) > 1
  51. def title(self):
  52. return self.lookup_title
  53. def choices(self, cl):
  54. yield {'selected': self.lookup_val is None,
  55. 'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
  56. 'display': _('All')}
  57. for val in self.lookup_choices:
  58. pk_val = getattr(val, self.field.rel.to._meta.pk.attname)
  59. yield {'selected': self.lookup_val == str(pk_val),
  60. 'query_string': cl.get_query_string( {self.lookup_kwarg: pk_val}),
  61. 'display': val}
  62. FilterSpec.register(lambda f: bool(f.rel), RelatedFilterSpec)
  63. class ChoicesFilterSpec(FilterSpec):
  64. def __init__(self, f, request, params):
  65. super(ChoicesFilterSpec, self).__init__(f, request, params)
  66. self.lookup_kwarg = '%s__exact' % f.name
  67. self.lookup_val = request.GET.get(self.lookup_kwarg, None)
  68. def choices(self, cl):
  69. yield {'selected': self.lookup_val is None,
  70. 'query_string': cl.get_query_string( {}, [self.lookup_kwarg]),
  71. 'display': _('All')}
  72. for k, v in self.field.choices:
  73. yield {'selected': str(k) == self.lookup_val,
  74. 'query_string': cl.get_query_string( {self.lookup_kwarg: k}),
  75. 'display': v}
  76. FilterSpec.register(lambda f: bool(f.choices), ChoicesFilterSpec)
  77. class DateFieldFilterSpec(FilterSpec):
  78. def __init__(self, f, request, params):
  79. super(DateFieldFilterSpec, self).__init__(f, request, params)
  80. self.field_generic = '%s__' % self.field.name
  81. self.date_params = dict([(k, v) for k, v in params.items() if k.startswith(self.field_generic)])
  82. today = datetime.date.today()
  83. one_week_ago = today - datetime.timedelta(days=7)
  84. today_str = isinstance(self.field, models.DateTimeField) and today.strftime('%Y-%m-%d 23:59:59') or today.strftime('%Y-%m-%d')
  85. self.links = (
  86. (_('Any date'), {}),
  87. (_('Today'), {'%s__year' % self.field.name: str(today.year),
  88. '%s__month' % self.field.name: str(today.month),
  89. '%s__day' % self.field.name: str(today.day)}),
  90. (_('Past 7 days'), {'%s__gte' % self.field.name: one_week_ago.strftime('%Y-%m-%d'),
  91. '%s__lte' % f.name: today_str}),
  92. (_('This month'), {'%s__year' % self.field.name: str(today.year),
  93. '%s__month' % f.name: str(today.month)}),
  94. (_('This year'), {'%s__year' % self.field.name: str(today.year)})
  95. )
  96. def title(self):
  97. return self.field.verbose_name
  98. def choices(self, cl):
  99. for title, param_dict in self.links:
  100. yield {'selected': self.date_params == param_dict,
  101. 'query_string': cl.get_query_string( param_dict, self.field_generic),
  102. 'display': title}
  103. FilterSpec.register(lambda f: isinstance(f, models.DateField), DateFieldFilterSpec)
  104. class BooleanFieldFilterSpec(FilterSpec):
  105. def __init__(self, f, request, params):
  106. super(BooleanFieldFilterSpec, self).__init__(f, request, params)
  107. self.lookup_kwarg = '%s__exact' % f.name
  108. self.lookup_kwarg2 = '%s__isnull' % f.name
  109. self.lookup_val = request.GET.get(self.lookup_kwarg, None)
  110. self.lookup_val2 = request.GET.get(self.lookup_kwarg2, None)
  111. def title(self):
  112. return self.field.verbose_name
  113. def choices(self, cl):
  114. for k, v in ((_('All'), None), (_('Yes'), '1'), (_('No'), '0')):
  115. yield {'selected': self.lookup_val == v and not self.lookup_val2,
  116. 'query_string': cl.get_query_string( {self.lookup_kwarg: v}, [self.lookup_kwarg2]),
  117. 'display': k}
  118. if isinstance(self.field, models.NullBooleanField):
  119. yield {'selected': self.lookup_val2 == 'True',
  120. 'query_string': cl.get_query_string( {self.lookup_kwarg2: 'True'}, [self.lookup_kwarg]),
  121. 'display': _('Unknown')}
  122. FilterSpec.register(lambda f: isinstance(f, models.BooleanField) or isinstance(f, models.NullBooleanField), BooleanFieldFilterSpec)