Search code examples
djangodjango-generic-views

Creating a generic search view in Django


I am struggling to create my custom generic view in django to easily create search pages for certain models. I'd like to use it like this:

class MyModelSearchView(SearchView):
    template_name = 'path/to/template.html'
    model = MyModel
    fields = ['name', 'email', 'whatever']

which will result in a view that returns a search form on GET and both form and results on POST. The fields specifies which fields of MyModel will be available for a user to search.

class SearchView(FormView):
    def get_form(self, form_class=None):
        # what I'v already tried:
        class SearchForm(ModelForm):
            class Meta:
                model = self.model
                fields = self.fields
        return SearchForm()

    def post(self, request, *args, **kwargs):
        # perform searching and return results

The problem with the code above is that form will not be submitted if certain fields are not be properly filled. User should be allowed to provide only part of fields to search but with the code I provided the form generated with ModelForm prevents that (for example because a field in a model cannot be blank).

My questions are:

  • Is it possible to generate a form based on a model to omit this behaviour?
  • Or is there any simpler way to create SearchView class?

I don't want to manually write forms if it's possible.


Solution

  • One way to accomplish this is to set blank=True on the field in MyModel, as indicated in the docs:

    If the model field has blank=True, then required is set to False on the form field. Otherwise, required=True.

    But for this to be a generic solution, you can't count on being able to modify the model fields. You can instead set the fields' required attribute to False immediately after the instance is created:

    class SearchForm(ModelForm):
    
        class Meta:
            model = self.model
            fields = self.fields
    
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            for (field_name, field) in self.fields.items():
                field.required = False