Compiling django filters into a variable and executing it at runtime?

16 views
Skip to first unread message

Joel

unread,
Aug 15, 2018, 10:30:37 AM8/15/18
to django...@googlegroups.com
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?

Matthew Pava

unread,
Aug 15, 2018, 10:40:52 AM8/15/18
to django...@googlegroups.com
I would take advantage of keyword arguments and dictionary expansion notation.
I would use something like this:

fields = ['cstid', 'age', 'gender', 'city']
search_query_and = {}
search_query_or = Q()
for f in fields:
if request.POST.get(f):
search_query_and[f"{f}__contains"] = request.POST.get(f).lower
search_query_or |= Q(**{f"{f}__contains": request.POST.get(f).lower})

result_and = customer.objects.filter(**search_query_and)
result_or = customer.objects.filter(search_query_or)
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/CAA%3Diw_-5WawhxK_ChrqaFQ9PCO1KPXUT-wWNeDcF31d%2B9D08vA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages