22
22
All This Does is filter things.
25
#FIXME: FilterGroup and ChoiceFilter have TONS of duplication. Combine these somehow and remove the redundancy!
26
#TODO: check over strings that should be unicode, make sure they are, mention in docstrings
27
#TODO: function to add filters to select by default in FilterGroup
25
30
from django.utils.safestring import mark_safe
27
#TODO: docstrings must specify that we need to convert lots of stuff to unicode strings
29
32
class Filter(object): #abstract
31
34
parent = None #immediate parent FilterContainer (relative to this object)
33
def __init__(self, id_str):
36
def __init__(self, id_str): #final
34
37
self.set_id(id_str)
40
def initialize(self): #abstract
39
42
Extend this function to do initialization stuff for an object
40
43
without interfering with the functionality of __init__.
44
def set_id(self, id_str):
47
def set_id(self, id_str): #final
45
48
self.id_str = id_str
50
def get_id(self): #final
50
def set_parent(self, parent):
53
def set_parent(self, parent): #final
51
54
self.parent = parent
56
def get_parent(self): #final
59
def get_system(self): #final
57
60
parent = self.get_parent()
59
62
if isinstance(parent, Filter):
72
75
full_name = "%s:%s" % (parent.get_id(), full_name)
75
def set_value(self, value):
78
def set_value(self, value): #abstract
77
80
Extend this to take a value passed down from the top, probably
78
81
from FilterSystem.update_http, and do something with it.
86
Extend this to returnthe value of this filter in the same format
87
it comes in. Used to generate URLs.
87
def get_value(self): #abstract
89
@return: the value of this instance, in a format native to its type
93
def get_value_string(self, value = None): #abstract
95
Extend this to return the value of this filter in the same
96
format it comes in. Used to generate URLs.
97
@param choices_dict: Optional value to be used instead of internal value
88
98
@return: a unicode string formatted for a URL's GET parameters
100
#For now, when extending, you must include the line:
101
#if not value: value = self.get_value()
92
def process_queryset(self, queryset):
106
@return: the url that toggles this filter's state
109
parent = self.get_parent()
110
if isinstance(self.get_parent(), FilterGroup):
111
href = parent.get_toggle_url(self.get_id())
112
print("get_toggle_url: %s" % href)
115
def process_queryset(self, queryset): #abstract
94
117
Extend this to manipulate a given queryset and then return it.
95
118
For example, queryset.filter(name__startswith = self.value)
99
122
return queryset.all()
124
def render(self): #final
126
@param context: a django.template.Context object. Expected fields are in module documentation.
127
@return: the default rendering of the filter itself in given context
102
129
return self.render_html()
104
def render_html(self):
131
def render_html(self): #abstract
106
133
Extend this to return the html output for the filter itself.
107
134
The output should be very simple and semantically meaningful,
108
135
with no specific concern about formatting. It will be
109
placed within other tags that describe its context, and it is
136
placed within other tags that describe its context, and it is
110
137
up to the template to describe style.
138
@param context: a django.template.Context object. Expected fields are in module documentation.
111
139
@return: a unicode string containing html representing this filter
113
return mark_safe(u'%s' % self.get_id())
141
#Would be nice to use django.template.Template here
142
return mark_safe(u'<a href="%s">(%s)</a>'
143
% (self.get_href(), self.get_id()))
115
145
class EditFilter(Filter): #abstract, extend in application
118
148
def set_value(self, value):
149
self.input_str = value
121
151
def get_value(self):
152
return self.input_str
154
def get_value_string(self, value = None):
155
if not value: value = self.get_value()
156
return self.input_str
124
158
class ChoiceFilter(Filter): #abstract, extend in application
125
159
choices_dict = dict()
129
163
self.selected_set = set([s for s in value.split(",") if s in self.choices_dict])
131
165
def get_value(self):
132
return ",".join(self.choices_dict)
166
return self.selected_set
168
def get_value_string(self, value = None):
169
if not value: value = self.get_value()
170
return ",".join(value)
134
172
def choice_is_selected(self, choice):
135
173
return choice in self.selected_set
141
179
if self.choice_is_selected(c):
142
180
c_render = "<em>%s</em>" % c_render
143
181
choices += c_render
144
return mark_safe(u'%s:<ul>%s</ul>' % (self.get_id(), choices))
183
return mark_safe(u'<a href="%s">%s</a>:<ul>%s</ul>' % (self.get_href(), self.get_id(), choices))
146
185
class FirstLetterFilter(Filter): #abstract, extend in application
159
198
filters_dict = dict() #to access children by id
161
def set_filters(self, filters_set):
200
def set_filters(self, filters_set): #final
163
202
Add a set of filters to be children of this one.
164
203
@param filter_set: a set of Filter objects
168
207
self.filters_dict[child.get_id()] = child
169
208
child.set_parent(self)
171
def get_filter(self, id_str):
210
def get_filter(self, id_str): #final
173
212
Get a child of this object, based on its id
174
213
@param id_str: a string that identifies the child
182
def get(self, full_name):
221
def get(self, full_name): #final
184
223
Finds a child based on its full name
185
224
@param full_name: an object's full name in the format parent:child
203
242
A collection of other filters. Children are enabled and disabled
204
243
freely. Their do_queryset functions are all mixed together into one.
206
#TODO: function to add filters to select by default
207
245
selected_set = set()
209
247
def __init__(self, id_str, filters_set):
216
254
def set_value(self, value):
218
Expects value to be a string, containing list of filters to
219
enable (separated by ",")
256
@param value: a string containing a comma separated list of filters to enable
221
258
self.selected_set = set([f for f in value.split(',') if f in self.filters_dict])
223
260
def get_value(self):
224
return ",".join(self.choices_dict)
261
return self.selected_set
263
def get_value_string(self, value = None):
264
if not value: value = self.get_value()
265
return ",".join(value)
267
def render_html(self):
269
for f in self.filters_dict:
270
f_render = self.filters_dict[f].render_html()
271
if self.filter_is_selected(f):
272
f_render = "<em>%s</em>" % f_render
273
internal += "<li>%s</li>" % f_render
275
return mark_safe("%s:<ul>%s</ul>" % (self.get_id(), internal))
226
277
def process_queryset(self, queryset):
228
279
Manipulates a queryset using the currently selected filters.
229
@param: queryset the queryset to be filtered
280
@param queryset: the queryset to be filtered
230
281
@return: a new queryset, filtered based on selected filters
232
283
for f in self.selected_set:
233
284
queryset = self.filters_dict[f].process_queryset(queryset) #returns something like QuerySet.filter(blah)
236
def render_html(self):
238
for f in self.filters_dict:
239
f_render = self.filters_dict[f].render() #Eek! What should I pass?!
240
if self.filter_is_selected(f):
241
f_render = "<em>%s</em>" % f_render
242
internal += "<li>%s</li>" % f_render
243
return mark_safe(u'Filters:<ul>%s</ul>' % internal)
287
def get_toggle_url(self, filter_id):
289
Returns the URL that should be used to toggle the given filter
290
that is a member of this FilterGroup.
292
select = set(copy(self.selected_set))
293
if filter_id in select:
294
select.remove(filter_id)
296
select.add(filter_id)
298
value = self.get_value_string(select)
299
url = self.get_system().url_with_value({self.get_full_name() : value})
246
303
class FilterSystem(FilterContainer): #abstract, extend in application
251
308
This object contains helper functions to deal with http requests
252
309
and for finding filters based on their full names.
311
request = None # current http request
313
def __init__(self): #final
256
314
self.initialize()
258
def initialize(self):
316
def initialize(self): #abstract
260
318
Extend this method to create all the filters inside
261
319
self.set_filters, and whatever else is appropriate.
265
def update_http(self, request):
267
Updates the state of all filters based on the HTTP request
268
@param request: Current HttpRequest object
323
def update_http(self, request): #final
325
Updates the state of all filters based on the HTTP request.
326
@param request: New HttpRequest object
328
self.request = request
270
329
for key in request.GET:
271
330
filter_object = self.get(key)
272
331
if filter_object:
273
filter_object.set_value(request.GET[key])
b'\\ No newline at end of file'
332
filter_object.set_value(request.GET[key])
334
def url_with_value(self, add_dict): #final
336
Returns the URL with a list of variables added or changed,
337
without affecting other variables or adding redundant entries.
338
Derived from <http://djangosnippets.org/snippets/1627/>
340
get = self.request.GET.copy()
343
get[key] = add_dict[key]
345
path = self.request.META['PATH_INFO']
347
#print "&".join(["%s=%s" % (key, value) for (key, value) in get.items() if value])
350
path += "?%s" % "&".join(["%s=%s" % (key, value) for (key, value) in get.items() if value])