Microfilters
Microfilters introduces two minimalist skeleton classes to build filters and filter sets in Python. I use them
mainly to implement filtering QuerySet objects in Django, but they can be safely used wherever the pattern fits.
Act of purpose
This was born out of me not quite feeling any of the currently available Django filtering apps, like
django-filter or
django-easyfilters. Don't get me wrong, both apps are well
crafted and useful in almost any case where generic and/or introspective QuerySet filtering is the feature you're
looking for. However, whenever I wanted to make customisations that go a little beyond the usual get_choices
override or lookup_type adjustment, at some point I always felt like imposing my requirements onto something, that
is not ment to provide that kind of flexibility.
What's shipping?
The suggestion of an approach. That's it. No magic out-of-the-box s**t involved here.
Requirements
- Python 2.6, 2.7, 3.2, 3.3
- six >= 1.3
Installation
With pip:
pip install microfilters
With setuptools:
git clone https://github.com/lukasbuenger/microfilters.git cd microfilters/ python setup.py
Filter and FilterSet API
Microfilters literally consists of two classes that do actually nothing but providing a simple interface that
IMHO covers most filtering purposes. Both classes only implement two methods (FilterSet being a subclass of
Filter):
sanitize- Gets passed a
value. Should raise an error ifvalueis not valid. Should return a evaluated and / or modified version ofvaluethat will be used for filtering. filter- The actual filter process. Accepts a
item_listand avalue(preferably sanitized) and should return a filtered version ofitem_listaccording tovalue.
The main difference between Filter and FilterSet is, that Filter objects should indeed filter lists, while
FilterSet objects represent a set of Filter objects and should delegate the filter operation to the
correspondent Filter if present. This leads to the following:
-
FilterSet.sanitizeshould accept and validate / modify a set of filter values instead of a single value. -
FilterSet.filtershould delegate the actual filtering of a givenitem_listto registeredFilterobjects based on the givenvalueset.
As mentioned before, a FilterSet represents a collection of Filter objects. We register
Filter objects to a FilterSet object by passing a dictionary to the constructor of the FilterSet. It may
look something like this:
filter_set = FilterSet({
'name': MyNameFilter(),
'city': MyCityFilter()
})
Now if we pass a dictionary with correspondent keys to this FilterSet, it delegates the handling of each value to
the registered Filter that got registered with the correspondent key:
filter_set.filter({
'name': 'Lukas',
'city': 'Zurich'
})
Examples
Both Filter and FilterSet literally cry for getting subclassed. Here are two simple implementation examples:
Filtering a list of strings:
class StringListFilter(Filter):
def sanitize(self, value):
if not isinstance(value, six.text_type):
raise Exception
return value
def filter(self, item_list, value):
return list(filter(lambda x: value in x, item_list))
filter_set = FilterSet({
'param1': StringListFilter(),
'param2': StringListFilter()
})
item_list = ['Lukas', 'Peter' , 'Barbara', 'Stuart']
print filter_set.filter(item_list, {
'param1': 'u'
})
# ['Lukas', 'Stuart']
print filter_set.filter(item_list, {
'param1': 'ar',
'param2': 'u'
})
# ['Stuart']
Filtering of Django QuerySet objects, based on fields:
class CharFilter(Filter):
def __init__(self, field_name):
self.field_name = field_name
def sanitize(self, value):
if not isinstance(value, six.text_type):
raise Exception
return value
def filter(self, item_list, value):
return item_list.filter(Q(**{'%s__contains' % self.field_name: value})
filter_set = FilterSet({
'name': CharFilter('username'),
'email': CharFilter('email')
})
queryset = User.objects.all()
print filter_set.filter(queryset, {
'name': 'lukasbuenger',
'email': 'lukasbuenger@gmail.com'
})
Running the tests
To run the unit tests, simply execute the runtests.py in the microfilter/runtests subdirectory in an environment
that has six installed:
python microfilter/runtests/runtests.py
