Man, I love
setattr. Today I had the pleasure on working on something really cool. A friend of mine recently wrote this amazing piece of boiler plate code that is very modular for a Django project. The intention was to create a drop in search form which would allow us to write queries with ease.
He basically has two data structures defined within the form and a super class that looks for those structures and creates queries from them. So if say you have something like this:
class MySearchForm(SearchFormMixin, ModelForm): class Meta: model = ModelClass fields = ('field1', 'field2', 'field3',) field_lookup = ( ('field1', 'field1__related_field'), ('field2', 'field2__related_field'),) def __init__(self, *args, **kwargs): super(MySearchForm, self).__init__(*args, **kwargs)
SearchFormMixin implements the field lookup by creating
Q objects from field_lookup tuple and kwargs from fields. Then essentially it boils down to doing this
However, here's the kicker. Say, if you want to implement a Global search, i.e. a search on EVERY field on the form, you would invariably choose
queryset.filter(Q(first_name__icontains='foo') | Q(last_name__contains='foo') | Q(date__gte='foo'))
You'll notice that this will throw an exception since date is expecting a
DateTime object. Similarly an
IntegerField is always expecting a int() base 10 object when searching. So how do you implement this programmatically when your form search is hardwired?
Well, that's when
setattr comes in. It allows you to set objects on class instances and classes in general and use them. So if I had to replace the methods in
SearchFormMixin, all I had to really do is, this
setattr(MySearchForm, 'filter', MyNewMixin().filter) setattr(MySearchForm, 'search', MyNewMixin().search)
MyNewMixin I just redefine the nature of the query lookup.
class MyNewMixin(object): def filter(self): """ Code to search objects independently """ ... def search(self): """ Code to trigger the filter """ ...
At this point the instance of class
MySearchForm is going to trigger
MyNewMixin().search() instead of
Pretty smart method I'd say.