dease
dease

Reputation: 3076

Django search objects by multiple keys

I am trying to prepare search form where user is able to type 1, 2 or all (3 in this case) search filters.

Lets say that search filters are: last name, phone and address. I am trying to filter queryset by:

if filterForm.is_valid():
    last_name = filterForm.cleaned_data.get('last_name')
    phone= filterForm.cleaned_data.get('phone')
    address = filterForm.cleaned_data.get('address')

    if last_name is None and phone is None and address is None:
        pass
        #we dont do search id db
    else:
        clients = Client.objects.filter(Q(last_name__contains=last_name) | Q(phone=phone) | Q(address__contains=address))

Each search key may be blank.

Unfortunately, it returns more results then expected. When I type in search filter "Example" as last name field, it returns all fields with this last name + many others rows.

Any idea how to fix this search issue?

Upvotes: 0

Views: 755

Answers (1)

HAL
HAL

Reputation: 2071

I believe that your search returns more results than expected when any of the search keys are blank since a blank key will match any row with a value.

By only filtering on keys that contains a value it should work better.

Here is one example of how it can be done:

if filterForm.is_valid():
    last_name = filterForm.cleaned_data.get('last_name')
    phone= filterForm.cleaned_data.get('phone')
    address = filterForm.cleaned_data.get('address')

    import operator

    predicates = []

    if last_name:
        predicates.append(Q(last_name__contains=last_name))
    if phone:
        predicates.append(Q(phone=phone))
    if address:
        predicates.append(Q(address__contains=address))

    if len(predicates) == 0:
        # Nothing to search for
        pass
    else:
        clients = Client.objects.filter(reduce(operator.or_, predicates))

The code above will dynamically add filters that shall be added to the query. The usage of oprator.or_ will concatenate the statements with OR (=at least one statement needs to be satisfied). If you instead want all statements to be satisfied you can use operator.and_ instead.

Upvotes: 1

Related Questions