priestc
priestc

Reputation: 35150

Best way to make a model search form?

I have this model:

class Aircraft(models.Model):
    model       =   models.CharField(max_length=64, blank=True)
    type        =   models.CharField(max_length=32)
    extra       =   models.CharField(max_length=32, blank=True)
    manufacturer    =   models.CharField(max_length=32)
    engine_type =   models.IntegerField("Engine Type", choices=ENGINE_TYPE, default=0)
    cat_class   =   models.IntegerField("Category/Class", choices=CAT_CLASSES, default=1)

And I have a "find aircraft" page where the user is presented with a form where they can enter data that will be used to find all aircraft that fits their criteria. For instance the user can enter "boeing" into a textbox and "jet" into the engine_type box, and it will display all boeing jets in the database. The way I'm doing this now is by this form:

class AircraftSearch(ModelForm):
    search = forms.CharField(max_length=100, required=False)
    class Meta:
        model = Aircraft
        fields = ('engine_type', 'cat_class', )

And then a (needlessly complex) view which converts the data from this form into a set of filter()'s which get added to Aircraft.objects.all(). (Instead of having 4 seperate search fields for each CharField, I have combined them all into one search field.)

This all works, but with one problem. If the user wants to exclude engine type from their search criteria, then they're screwed because "Any" is not a valid choice for the engine type field. I'm going to have to create a new field/widget for engine type and category/class to include "Any", which kind of defeats the purpose of using a model view in the first place

I'm curious. Is there a better way? This seems like a very common task which has to have been tackled by someone else already, but a google search brings up nothing.

Upvotes: 5

Views: 7401

Answers (2)

rob
rob

Reputation: 37634

I think you should not use a ModelForm.
Typical ModelForm usescase is data manipulation, and not its search.

Instead, make a completely new form, based on the fields you need and with checkboxes that user will (de-)select to disable searching a particular field.
Of course, you still should use the choices defined in the model, by simply importing the file and using that list.

Upvotes: 3

tghw
tghw

Reputation: 25303

Functionally, "any" would be achieved by just not including that particular search vector in the filtering.

Generally, ModelForms are for creating and editing models; in this case, I'm not sure it's helping you much more than just doing a regular form:

class AircraftSearch(forms.Form):
    search = forms.CharField(max_length=100, required=False)
    engine_type = forms.ChoiceField(choices=ENGINE_TYPE)
    cat_class = forms.ChoiceField(choices=CAT_CLASS)

To do the search, you then just filter when fields are non-empty:

def search(request):
    if request.method == 'POST':
        results = Aircraft.objects.all()

        search = request.POST.get('search', None)
        if search:
            results = results.filter(Q(model=search)|Q(type=search)|Q(extra=search)|Q(manufacturer=search))

        engine_type = request.POST.get('engine_type', None)
        if engine_type:
            results = results.filter(engine_type=engine_type)

        cat_class = request.POST.get('cat_class', None)
        if cat_class:
            results = results.filter(cat_class=cat_class)

        return render_to_response('aircraft.html', {'form': AircraftSearch(request.POST), 'aircraft': results})

    return render_to_response('aircraft.html', {'form': AircraftSearch()})

Upvotes: 8

Related Questions