I'm trying to write a search function for my model. A customer has the following fields:
cstid = models.AutoField(primary_key=True, unique=True)
name = models.CharField(max_length=35)
age=models.IntegerField()
gender = models.CharField(max_length=10, default='')
mobile = models.CharField(max_length=15, default='')
email = models.CharField(max_length=50, default='')
address = models.CharField(max_length=80, default='')
city = models.CharField(max_length=25, default='')
My html form will submit the following data by POST via an ajax call to django:
var data = {
"name": name,
"age": age,
"email": email,
"address": address,
"phone": phone,
"city": city
};
data = $(this).serialize() + "&" + $.param(data);
These will be verified in the django view, and a search query performed, as below:
def searchpat_for_docwise_appt (request):
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q
from django.db.models import CharField
from django.db.models.functions import Lower
CharField.register_lookup(Lower, "lower")
if request.method == 'POST':
name = request.POST.get('name')
age = request.POST.get('age')
# gender = request.POST.get('gender')
phone = request.POST.get('phone')
email = request.POST.get('email').lower()
address = request.POST.get('address')
city = request.POST.get('city')
if age.isdigit():
ANDSearchResult = customer.objects.filter(name__lower__contains=name.lower(), age=age, mobile__contains=phone, email__lower__contains=email.lower(
), address__lower__contains=address.lower(), city__lower__contains=city.lower())
else:
ANDSearchResult = customer.objects.filter(name__lower__contains=name.lower(), mobile__contains=phone, email__lower__contains=email.lower(
), address__lower__contains=address.lower(), city__lower__contains=city.lower())
if len(ANDSearchResult) < 1:
errmsg = 'No search results for AND search'
print("Error message is <%s>" % errmsg)
else:
print ("ANDSearchResult is <%s>" % ANDSearchResult)
errmsg = ''
if age.isdigit():
ORSearchResult = customer.objects.filter(
Q(name__lower__contains=name.lower()) | Q(age=age) | Q(
mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))
else:
ORSearchResult = customer.objects.filter(
Q(name__lower__contains=name.lower()) | Q(
mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))
if len(ORSearchResult) < 1:
errmsg = errmsg + 'No search results for OR search'
print("Error message is <%s>" % errmsg)
else:
print ("ORSearchResult is <%s>" % ORSearchResult)
print(errmsg)
return HttpResponse(errmsg)
else:
errmsg = 'No correct POST request. No valid response.'
print("Error message is <%s>" % errmsg)
return HttpResponse(errmsg)
As you can note above, I am doing two different queries simultaneously. I want an AND operator like query where search has to satisfy all input parameters. I also need an OR operator which should provide a result if any of the fields matches the entered string for the corresponding field. By this I mean that if a patient has a name Jeff with age 56, and a patient Kane with age 23; and I enter jef in the name field and 23 in the age field, both should be listed.
The problem arises when there are empty strings in the search boxes. An OR search with an empty string gives all objects as results. Obviously I dont want to return a patient record just because my search strings are null.
Other than the long winded solution of checking for all combinations of search queries like this:
if (name != '' and age != '' and phone != '' and email != '' and address != '' and city != '':
ORSearchResult = customer.objects.filter(Q(name__lower__contains=name.lower()) | Q(age=age) | Q(mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))
elif (age != '' and phone != '' and email != '' and address != '' and city != '':
ORSearchResult = customer.objects.filter(Q(age=age) | Q(mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))
elif (phone != '' and email != '' and address != '' and city != '':
ORSearchResult = customer.objects.filter(Q(mobile__contains=phone) | Q(email__lower__contains=email.lower()) | Q(address__lower__contains=address.lower()) | Q(city__lower__contains=city.lower()))
.... Other permutations ...
What is a better solution? An obvious idea that comes to mind is, to try to compile these filters into a variable and pass it to the filter function. Am I on the right track? How can I do this?