Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django model search form

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)
like image 903
user3138929 Avatar asked Feb 18 '14 09:02

user3138929


People also ask

What is form AS_P in Django?

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.

What is ModelForm in Django?

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.


1 Answers

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
like image 134
petkostas Avatar answered Sep 30 '22 15:09

petkostas