6

Say, I have a model with a text field:

class SomeModel
    keyword=models.CharField(null=True, max_length=255)

Now, I know how to check if a parameter string (lets call the variable "querystring" is contained in the field keyword:

results = SomeModel.objects.filter(keyword_icontains=querystring).all()

which I found in the django docs

Question, how do I filter for the objects whose field values are contained in the querystring variable?

Apologies if my question is confusing... maybe an example will clarify... In django docs, if my keyword field contains,for example, 'python-django', then, for a querystring that contains 'django', I can extract the object that contains that field with an

results=SomeModel.objects.filter(keyword_icontains=querystring).all()
or results=SomeModel.objets.filter(keyword_icontains='django').all()

But say, I want to extract all rows/objects whose keyword field is contained in a querystring? For example, if querystring contains 'In django, how do I create a filter'? then I want results to contain all objects whose keyword fields have the values 'django', 'filter', etc...

RVC
  • 420
  • 1
  • 4
  • 12
  • 1
    Querystring containing the field? As in the value of querystring containing field name of the model? – Rajesh Yogeshwar Jun 13 '16 at 04:29
  • @RajeshYogeshwar, as in the value of the querystring containing the value of the field of the model... – RVC Jun 13 '16 at 04:32
  • Your current example is incorrect, unless `querystring` is actually the string `"querystring"`. And the SQL equivalent is then `SELECT ... WHERE keyword IKE '%querystring%'`. Possibly, that is causing confusion as to what your question is, and the likely answer. –  Jun 13 '16 at 04:38
  • The bottom SQL query translates to `results = SomeModel.objects.filter(querystring_icontains="keyword").all()` –  Jun 13 '16 at 04:39
  • 1
    @RVC thats what I asked. You are trying to do a LIKE query on the field name itself. May I know exactly the purpose of same to give better understanding of what you are trying to achieve? – Rajesh Yogeshwar Jun 13 '16 at 04:39
  • @RajeshYogeshwar, the model contains objects whose keyword fields contain keywords like "garbage", "sewage", "janitorial", or "delivery". When the user inputs "garbage delivery" I want to get the objects whose keyword fields are "garbage" and "delivery". Hope that helps. – RVC Jun 13 '16 at 05:52
  • @Evert, thank you for your input. I've edited the question to be clearer. I hope that helps. – RVC Jun 13 '16 at 05:53
  • "When the user inputs "garbage delivery" I want to get the objects whose keyword fields are "garbage" and "delivery".". Split the user input on whitespace, and loop over the individual words, matching them each time with `__icontains` like you're doing now. –  Jun 13 '16 at 06:03
  • 1
    You're matching on words from the input sentence, afaict. But you'll have to be specific in your question whether that's really the case. What about punctuation or whitespace? Include or exclude those? What about possessives like "Searching in StackOverflow's questions" or abbrevations like "isn't"? –  Jun 13 '16 at 06:06
  • I second @Evert here, there are lot of corner cases here, how are you going to limit them – Rajesh Yogeshwar Jun 13 '16 at 06:07
  • @Evert, I guess your suggestion is good enough. My client didn't specify, so I guess, I'll just have to explain the limits of their specs. If you'll put your suggestion in as an answer, I'll accept it. Thanks! – RVC Jun 13 '16 at 06:18
  • Accepted answer fails in cases where e.g. the search string is "garbage delivery" and the object keywords are "bage del", "age", "liver". All those objects should be selected by the query. I haven't figured out a solution using the standard django ORM but see [this post](https://stackoverflow.com/questions/4916195/mysql-select-query-string-matching) for anyone who doesn't mind using raw SQL. – Zevgon Dec 14 '18 at 21:11

2 Answers2

8

You'll have to split your input into words (depending on your definition of "word"), and then iterate over them, concatenating the various resulting querysets (most of which are likely to be empty).

To split, you could use a regex, catching all letters and the apostrophe (but you'll miss a smart-quote, if someone uses that as input instead of the standard ASCII apostrophe):

words = re.split(r"[^A-Za-z']+", querystring)

Then loop and filter:

query = Q()  # empty Q object
for word in words:
    # 'or' the queries together
    query |= Q(keyword_icontains=word)
results = SomeModel.objects.filter(query).all()

The above code is untested, and I got the idea to 'or' the queries and an empty Q() from this answer.

Depending on your next step(s), evaluating the query directly in each loop step, appending to a results list, might work better for you (using e.g. itertools.chain, as per this answer).

Community
  • 1
  • 1
6

Here is a solution that will select SomeModel rows whose keyword is any substring of the querystring, not just complete words:

SomeModel.objects\
    .annotate(querystring=Value(querystring, output_field=CharField()))\
    .filter(querystring__icontains=F('keyword'))

See docs for info about annotate, Value expressions and F expressions

Zevgon
  • 556
  • 4
  • 13