The Python Oracle

In Django, how does one filter a QuerySet with dynamic field lookups?

--------------------------------------------------
Rise to the top 3% as a developer or hire one of them at Toptal: https://topt.al/25cXVn
--------------------------------------------------

Music by Eric Matyas
https://www.soundimage.org
Track title: Puzzling Curiosities

--

Chapters
00:00 In Django, How Does One Filter A Queryset With Dynamic Field Lookups?
00:31 Accepted Answer Score 366
00:48 Answer 2 Score 9
01:47 Answer 3 Score 5
01:57 Answer 4 Score 7
02:44 Thank you

--

Full question
https://stackoverflow.com/questions/3107...

--

Content licensed under CC BY-SA
https://meta.stackexchange.com/help/lice...

--

Tags
#python #django #djangomodels

#avk47



ACCEPTED ANSWER

Score 366


Python's argument expansion may be used to solve this problem:

kwargs = {
    '{0}__{1}'.format('name', 'startswith'): 'A',
    '{0}__{1}'.format('name', 'endswith'): 'Z'
}

Person.objects.filter(**kwargs)

This is a very common and useful Python idiom.




ANSWER 2

Score 9


A simplified example:

In a Django survey app, I wanted an HTML select list showing registered users. But because we have 5000 registered users, I needed a way to filter that list based on query criteria (such as just people who completed a certain workshop). In order for the survey element to be re-usable, I needed for the person creating the survey question to be able to attach those criteria to that question (don't want to hard-code the query into the app).

The solution I came up with isn't 100% user friendly (requires help from a tech person to create the query) but it does solve the problem. When creating the question, the editor can enter a dictionary into a custom field, e.g.:

{'is_staff':True,'last_name__startswith':'A',}

That string is stored in the database. In the view code, it comes back in as self.question.custom_query . The value of that is a string that looks like a dictionary. We turn it back into a real dictionary with eval() and then stuff it into the queryset with **kwargs:

kwargs = eval(self.question.custom_query)
user_list = User.objects.filter(**kwargs).order_by("last_name")   



ANSWER 3

Score 7


Additionally to extend on previous answer that made some requests for further code elements I am adding some working code that I am using in my code with Q. Let's say that I in my request it is possible to have or not filter on fields like:

publisher_id
date_from
date_until

Those fields can appear in query but they may also be missed.

This is how I am building filters based on those fields on an aggregated query that cannot be further filtered after the initial queryset execution:

# prepare filters to apply to queryset
filters = {}
if publisher_id:
    filters['publisher_id'] = publisher_id
if date_from:
    filters['metric_date__gte'] = date_from
if date_until:
    filters['metric_date__lte'] = date_until

filter_q = Q(**filters)

queryset = Something.objects.filter(filter_q)...

Hope this helps since I've spent quite some time to dig this up.

Edit:

As an additional benefit, you can use lists too. For previous example, if instead of publisher_id you have a list called publisher_ids, than you could use this piece of code:

if publisher_ids:
    filters['publisher_id__in'] = publisher_ids



ANSWER 4

Score 5


Django.db.models.Q is exactly what you want in a Django way.