django model search form
django, forms, model, search
Solution
I had a similar case like yours (real estate project), I ended up with the following approach, you can refine this to your needs...I removed select_related and prefetch_related models for easier reading
properties/forms.py:
class SearchPropertyForm(forms.Form):
property_type = forms.ModelChoiceField(label=_("Property Type"), queryset=HouseType.objects.all(),widget=forms.Select(attrs={'class':'form-control input-sm'}))
location = forms.ModelChoiceField(label=_('Location'), queryset=HouseLocation.objects.all(), widget=forms.Select(attrs={'class':'form-control input-sm'}))
Then in the properties/views.py
# Create a Mixin to inject the search form in our context
class SeachPropertyMixin(object):
def get_context_data(self, **kwargs):
context = super(SeachPropertyMixin, self).get_context_data(**kwargs)
context['search_property_form'] = SearchPropertyForm()
return context
In your actual view (I apply the search form as a sidebar element in my detailview only:
# Use Class Based views, saves you a great deal of repeating code...
class PropertyView(SeachPropertyMixin,DetailView):
template_name = 'properties/view.html'
context_object_name = 'house'
...
queryset = HouseModel.objects.select_related(...).prefetch_related(...).filter(flag_active=True, flag_status='a')
Finally your search result view (this is performed as GET request, since we are not altering any data in our DB, we stick to the GET method):
# Search results should return a ListView, here is how we implement it:
class PropertySearchResultView(ListView):
template_name = "properties/propertysearchresults.html"
context_object_name = 'houses'
paginate_by = 6
queryset = HouseModel.objects.select_related(...).prefetch_related(...).order_by('-sale_price').filter(flag_active=True, flag_status='a')
def get_queryset(self):
qs = super(PropertySearchResultView,self).get_queryset()
property_type = self.request.GET.get('property_type')
location = self.request.GET.get('location')
'''
Start Chaining the filters based on the input, this way if the user has not
selected a filter it wont be used.
'''
if property_type != '' and property_type is not None:
qs = qs.filter(housetype=property_type)
if location != '' and location is not None:
qs = qs.filter(location=location)
return qs
def get_context_data(self, **kwargs):
context = super(PropertySearchResultView, self).get_context_data()
'''
Add the current request to the context
'''
context['current_request'] = self.request.META['QUERY_STRING']
return context
Problem
Firstly, I did my homework and looked around before posting! My question seems like a very basic thing that must’ve been covered before. I'm now looking at Django-filter as a potential solution, but would like some advice on if this is the right way to go and if there any other solutions. I have a Django app wit 10 models, each model has a few fields. Most fields are `ChoiceField` that users populate using forms with the default `select` widget. There is a separate form for each model. I want to create a separate form for each model (in separate views) that users will use to search the database. The search form will contain only drop-down boxes (the `select` widgets) with the same choices as the forms used to populate the database with the addition of the “any” option. I know how to use `.object.filter()`, however the “any” option would correspond to not include specific fields in the filter and I'm not sure how to add model fields to the filter based on users’ selection I briefly looked at Haystack as an option but it seems to be made for full text search rather than “model filed search” I'm after. Sample model (simplified): ``` class Property(): TYPE_CHOICES = (‘apartment’, ‘house’, ‘flat’) type = charfield(choices=TYPE_CHOICES) LOC_CHOICES = (‘Brussels’, ‘London’, ‘Dublin’, ‘Paris’) location = charfield(choices=LOC_CHOICES) price = PostivieInteger() ``` Users can select only “type”, only “location” or both (not making selection is equal to ANY) in which case I end up with 3 different filters: ``` Property.objects.filter(type=’apartment’) Property.objects.filter(location=’Dublin’) Property.objects.filter(type=’apartment’, location=’Dublin’) ``` The main question: django-filter the best option? ``` Question 1: what’s the best option of accomplishing this overall? Question 2: how do I add model fields to the filter based on user’s form selection? Question 3: how do I do the filter based on user selection? (I know how to use .filter(price_lt=).exclude(price_gt=) but again how do I do it dynamically based on selection as “ANY” would mean this is not included in the query) ```