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)
Django forms are an advanced set of HTML forms that can be created using python and support all features of HTML forms in a pythonic way.
Django Model Form It is a class which is used to create an HTML form by using the Model. It is an efficient way to create a form without writing HTML code. Django automatically does it for us to reduce the application development time.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With