I am trying to build a relatively simple search form that allows the user to search one of three search fields or any combination of the three. The result should filter the queryset based upon the terms given. Here is my form from search_form.html:
<form class="form-horizontal" action="/search/" method="get">
<legend>Generate a Quiz:</legend>
<div class="control-group">
<label class="control-label" for="q">Search term</label>
<div class="controls">
<input type="text" name="q" placeholder="Search term" autocomplete="on"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="yr">Year</label>
<div class="controls">
<input type="text" name="yr" placeholder="Year" autocomplete="on"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="ct">Category</label>
<div class="controls">
<input type="text" name="ct" placeholder="Category" autocomplete="on"/>
</div>
</div>
<div class="controls">
<button type="submit" class="btn btn-primary">Fetch »</button>
</div>
</form>
And here is my view:
def search(request):
error = False
if 'q' in request.GET:
q = request.GET['q']
yr = request.GET['yr']
ct = request.GET['ct']
if not ((q or yr) or ct):
error = True
else:
question_set = Uroquiz.objects.filter(question__icontains=q).filter(year=yr).filter(cat_icontains=ct)
paginator = Paginator(question_set, 1)
page = request.GET.get('page')
try:
question_set = paginator.page(page)
except PageNotAnInteger:
question_set = paginator.page(1)
except EmptyPage:
question_set = paginator.page(paginator.num_pages)
return render_to_response('search_results.html',
{'question': question_set, 'query': q, 'year': yr, 'ct': ct})
return render_to_response('search_form.html', {'error': error})
Problems:
-
When I built this with just the ‘q’ filed, it worked fine. When I chained in the ‘yr’ field to my filter, it works fine as long as there is a year entered into the form. If year (an integer field in my database) was empty, it would raise a ValueError invalid literal for int() with base 10: ”.
-
When I chained in the ‘ct’ field, and try to search all fields, I get the error: Cannot resolve keyword ‘cat_icontains’ into field. I can change the filter to (cat=ct) and it works fine, but I should be able to use _icontains on the ‘cat’ field from my database.
I’m sure I am missing something basic, any help is appreciated.
Update:
Here is my current solution based on the answers below, that allows searching on any combination of fields:
def search(request):
q = request.GET.get('q')
yr = request.GET.get('yr', 0)
ct = request.GET.get('ct')
question_set = Uroquiz.objects.all()
if q:
if not (yr or ct):
question_set = Uroquiz.objects.filter(question__icontains=q)
if yr:
if not (q or ct):
question_set = Uroquiz.objects.filter(year=yr)
if ct:
if not (q or yr):
question_set = Uroquiz.objects.filter(cat__icontains=ct)
if (q and yr):
if not ct:
question_set = Uroquiz.objects.filter(question__icontains=q).filter(year=yr)
if (q and ct):
if not yr:
question_set = Uroquiz.objects.filter(question__icontains=q).filter(cat__icontains=ct)
if (yr and ct):
if not q:
question_set = Uroquiz.objects.filter(year=yr).filter(cat__icontains=ct)
if ((q and yr) and ct):
question_set = Uroquiz.objects.filter(question__icontains=q).filter(year=yr).filter(cat__icontains=ct)
if not ((q or yr) or ct):
return render_to_response('search_form.html')
paginator = Paginator(question_set, 1)
page = request.GET.get('page')
try:
question_set = paginator.page(page)
except PageNotAnInteger:
question_set = paginator.page(1)
except EmptyPage:
question_set = paginator.page(paginator.num_pages)
return render_to_response('search_results.html',
{'question': question_set, 'query': q, 'yr': yr, 'ct': ct})
This seems to be a ‘busy’ way to solve this. Is there a more idiomatic/pythonic way of accomplishing this search result?
It is not guarenteed that a user will submit
q,yr, andct. You cannot trust your users data. If a user didn’t submit any of those params i think it will result in an errorKeyErroryou could set default for these using
getAlso you could build your queryset as each param is found. remember querysets are laziy
for your second question, I believe, it is syntax related
cat__icontains2 underscores (instead offilter(cat_icontains=ct), like you have in your code)