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 stringargument_list = []fields = ('search_field1','search_field2','search_field3')for query in query_string.split(' '): # split search wordsfor 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
Thanks for this! I was trying to do exactly the same thing. This has saved me a lot of time.
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!
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!
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 😀
nice one
would be better without typos though..
Thanks! Worked for me.
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?
Can you clarify? What are you trying to do?
A few more use cases would help : )
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!
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.thismonthThanks! 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.
No prob! I didn’t have that context and was trying to understand. Now I get it ; )
Thanks! This solved a tricky problem I’ve been fighting with…
How to render form for choosing filter in template?