1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
class SearchLet(Form): district = Field(widget=SelectMultiple(choices=DISTRICTS, required=False) min_price = IntegerField(widget=TextInput(required=False) max_price = IntegerField(widget=TextInput(required=False) #def clean_district... def search_let(request): if request.method == "POST": form = SearchLet(request.POST) if form.is_valid(): #bind data to dict keys qparams = dict( district = form.cleaned_data["district"], min_price = form.cleaned_data["min_price"], max_price = form.cleaned_data["max_price"], ) #keep list Q qsets = dict( district = Q(property__district__in = qparams["district"]), min_price = Q(property__houselet__rent_price__gte = qparams["min_price"]), max_price = Q(property__houselet__rent_price__lte = qparams["max_price"]) ) #filter out the Q that have None values final_query = [qsets[k] for k in qsets if qparams[k] is not None] custom_search_results = Ad.objects.select_related().filter(*final_query) context = dict(form=form, custom_search_results=custom_search_results) else: pass #handle invalid form else: pass #handle GET return render_to_response("search_house_let.html", context)
Refactorings
No refactoring yet !
akaihola
October 9, 2008, October 09, 2008 12:12, permalink
Closing paretheses missing from fields in class SearchLet.
Usually people "from django import forms" instead of objects inside the module.
"required=" is a keyword argument of the field, not the widget.
The default widget for IntegerField is TextInput. No need to specify that.
Here's my stab at adding reusability:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
#!/usr/bin/python from django import forms from django.db.models.query import QuerySet class EasyFilter(object): def __init__(self, **kwargs): try: self.query_kwargs = dict((self.filters[key], value) for key, value in kwargs.iteritems()) except KeyError: raise KeyError('Invalid field(s) in %r' % kwargs) def get_query_set(self): return self.model._default_manager.filter(**self.query_kwargs) class AdFilter(EasyFilter): model = Ad filters = dict(district='property__district__in', min_price='property__houselet__rent_price__gte', max_price='property__houselet__rent_price__lte') class SearchLet(forms.Form): district = forms.Field(widget=forms.SelectMultiple(choices=DISTRICTS), required=False) min_price = forms.IntegerField(required=False) max_price = forms.IntegerField(required=False) #def clean_district... def search_let(request): if request.method == "POST": form = SearchLet(request.POST) if form.is_valid(): custom_search_results = Adfilter(**form.cleaned_data).get_query_set().select_related() context = dict(form=form, custom_search_results=custom_search_results) else: pass #handle invalid form else: pass #handle GET return render_to_response("search_house_let.html", context)
I'm using the following code (shortened for readability) to search inside Django models.
On occasions, i had people telling me they would use the same principle but would make it better, but they didn't bother entering into details.
The code below does not "scale" too well, by that meaning that it's hard to adapt to different models, it's difficult to test and get's messy when you start adding many fields.
Can you make it better? This is the piece of Django i'm less satisfied with, but having never heard anybody complaining too loudly about it i think i might be missing something.
One thing i have in mind might be making a custom manager? Would that fit this use case?