1

How would I go about using the django.db.models Q module to query multiple lines of input from a list of data using a <textarea> html input field? I can query single objects just fine using a normal html <input> field. I've tried using the same code as my input field, except when requesting the input data, I attempt to split the lines like so:

  def search_list(request):
    template = 'search_result.html'
    query = request.GET.get('q').split('\n')
    for each in query:
        if each:
            results = Product.objects.filter(Q(name__icontains=each))

This did not work of course. My code to query one line of data (that works) is like this:

  def search(request):
    template = 'search_result.html'
    query = request.GET.get('q')
    if query:
        results = Product.objects.filter(Q(name__icontains=query))

I basically just want to search my database for a list of data users input into a list, and return all of those results with one query. Your help would be much appreciated. Thanks.

juju
  • 884
  • 1
  • 9
  • 31
  • Should this be **and** or **or** logic: is any of the elements sufficient, or should the text contain *all*? – Willem Van Onsem Jun 18 '18 at 16:54
  • Furthermore what should happen in case no such query is given? – Willem Van Onsem Jun 18 '18 at 16:55
  • @WillemVanOnsem It should be "or" logic. It should match any words present in the names/phrases in my database for each line of text and return results. If no query is given, or if an incorrect query is given, I'll have all queries matching each line displayed on a page, and some type of error message in place of the invalid query. Let me know if I didn't answer your question correctly. – juju Jun 18 '18 at 17:07

1 Answers1

1

Based on your comments, you want to implement OR-logic for the given q string.

We can create such Q object by reduce-ing a list of Q objects that each specify a Q(name__icontains=...) constraint. We reduce this with a "logical or" (a pipe in Python |), like:

from django.db.models import Q
from functools import reduce
from operator import or_

def search_list(request):
    template = 'search_result.html'
    results = Product.objects.all()
    error = None
    query = request.GET.get('q')
    if query:
        query = query.split('\n')
    else:
        error = 'No query specified'
    if query:
        results = results.filter(
            reduce(or_, (Q(name__icontains=itm.strip()) for itm in query))
        )
    elif not error:
        error = 'Empty query'
    some_context = {
        'results' : results,
        'error': error
    }
    return render(request, 'app/some_template.html', some_context)

Here we thus first check if q exists and is not the empty string. If that is the case, the error is 'No query specified'. In case there is a query, we split that query, next we check if there is at least one element in the query. If not, our error is 'Empty query' (note that this can not happen with an ordinary .split('\n'), but perhaps you postprocess the list, and for example remove the empty elements).

In case there are elements in query, we perform the reduce(..) function, and thus filter the Products.

Finally here we return a render(..)ed response with some_template.html, and a context that here contains the error, and the result.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Great. This looks to be exactly what I'm looking for. I'll try this out as soon as I can and report back. Thanks a bunch. – juju Jun 18 '18 at 17:17
  • I am having some troubles. The code works for one object, but returns nothing when I input two lines of data. I think that when i get the query, it is returning 1 object as opposed to splitting them by line. I'm getting a query for redgreen for example, instead of one for red, and one for green. Am I using the incorrect html field? It works when I query 1 object using `` but zero when I add a new line. The code seems like it should work otherwise. – juju Jun 18 '18 at 21:26
  • Can you add a `print(query)` statement for `query` (after split, so before the second `if`), and share what the value is. – Willem Van Onsem Jun 18 '18 at 21:26
  • Nothing is being printed to the console. The Get request is going through, and is as follows: `"GET /results/?q=apple%0D%0Apink HTTP/1.1" 200 14350` – juju Jun 18 '18 at 21:42
  • @juju: ah, thats a carriage return. What is we `.strip(..)` all elements? – Willem Van Onsem Jun 18 '18 at 21:44
  • Ok great! That worked perfectly! Thank you very much :) – juju Jun 19 '18 at 02:20