Django / Python — Dynamically Create Queries from a String and the OR operator

Dynamically create queries from strings in django.

My problem: I wanted to make a searchable, sorted list of an arbitrary amount of fields to check against.

Normally, we’d use a  Q object to get to the | “or” operator. Our search might look like the following:

Person.objects.filter( Q(first_name__icontains='Yuji') | Q(last_name__icontains='Tomita')

What if I don’t want to hard code the field names? I want to use dynamic field names so I can use a basic factory on multiple models very easily.

With much needed prodding in the right direction by mattmcc, I figured it out.

The key was how to use keyword arguments as strings.

Person.objects.filter(first_name__icontains='Yuji')
is equal to:
Person.objects.filter( **{'first_name__icontains':'Yuji'})

With this knowledge, and operator.or_, I could do a Q and | lookup via strings.

query_string = "Some Name" # the search string
argument_list = []
fields = ('search_field1','search_field2','search_field3')
for query in query_string.split(' '): # split search words
for field in fields:
argument_list.append( Q(**{field+'__icontains': query} )
query = MyModel.objects.filter(  reduce(operator.or_, argument_list)  ) # join the arguments in the list with the or operator

15 Comments

  1. sublimevelo says:

    Thanks for this! I was trying to do exactly the same thing. This has saved me a lot of time.

  2. Yuji says:

    Hey man, I’m glad it helped. I didn’t write it very well, since I had already spent hours trying to figure it out.

    Enjoy!

  3. Really useful post. I didn’t realize that it’s possible to pass a dictionary to .filter() instead of using the “shorthand” (which until reading this post was the only syntax I’d seen). It’s great to discover that it’s possible to reference field names dynamically while looping through lists!

  4. Yuji says:

    I’m really glad it helped! This was a useful discovery since I wasn’t aware of the **{ } syntax.

    I am absolutely positive this will pop up again in my Python travels 😀

  5. josi says:

    nice one
    would be better without typos though..

  6. Lee Campbell says:

    Thanks! Worked for me.

  7. Jenni says:

    What if I want to reference a field NOT in a query? Like
    thismonth = now.strftime(“%b”).lower() # this returns the abbreviated month (e.g. “nov”)
    ff = something.objects.get(id__exact == 1)
    print ff.thismonth #<<<— Here is where I would like to have "thismonth" be variable as there is one field for each month in the table.

    Any ideas?

    1. Yuji says:

      Can you clarify? What are you trying to do?

      A few more use cases would help : )

      1. Jenni says:

        I have a table, say “months”, with fields: jan, feb, mar, apr, etc. I want to print out the value of the current month, which I can do by:

        ff = months.objects.get(id__exact = 1) #purely for example here!)
        print ff.nov

        But I want it to work for the current month, without me having to have a pile of if statements, something like this:

        thismonth = now.strftime(“%b”).lower() #this month = “nov” right now
        ff = months.objects.get(id__exact = 1) #purely for example here!)
        print ff.thismonth

        This last line doesn’t work, obviously, but I want it to have the same effect as “print ff.nov”. I want to use the variable “thismonth” to tell python/django which database field to look at.

        Not sure how to be more clear!

    2. Yuji says:

      Ah, well that doesn’t really have anything to do with querysets 😛

      I’d use an instance property or method:

      class MyModel(...):
          @property
          def thismonth(self):
               return getattr(self, now().strftime("%b").lower(), None)
               
               
      model = MyModel.objects.get(id=1)
      print model.thismonth
      
  8. Jenni says:

    Thanks! I realize this has nothing to do with querysets, except that when I was searching for the solution to this, I kept finding answers to how to use variables within the querysets instead of what I was looking for. So to ME it’s related 🙂 Cheers.

    1. Yuji says:

      No prob! I didn’t have that context and was trying to understand. Now I get it ; )

  9. Rachel says:

    Thanks! This solved a tricky problem I’ve been fighting with…

  10. Damian says:

    How to render form for choosing filter in template?

Leave a reply to Rachel Cancel reply