We can unroll the list, and define a set of __contains
predicate with or-logic:
from django.db.models import Q
from functools import reduce
from operator import or_
Phrase.objects.filter(
reduce(or_, (Q(name__contains=e) for e in phrases_filter))
)
In the case of the given phrases_filter
, we will generate something equivalent to:
Phrase.objects.filter(
Q(name__contains='xx') | Q(name__contains='yy')
)
So reduce(..)
will operate on a generator of Q(..)
objects, here [Q(name__contains='xx'), Q(name__contains='yy')]
. The reduce(..)
works like what is known in functional programming as a "fold" (which is a special case of a catamorphism).
We thus will add or_
s in between, resulting in Q(name__contains='xx') | Q(name__contains='yy')
. This is an object that basically encodes a filter condition: it means that the name
column should __contains
the substring 'xx'
, or the substring 'yy'
.
Note: in case you want to match case insensitive (so rows with XX
, or Xx
, or xX
, or xx
are all candidates), then replace __contains
with __icontains
.
EDIT: based on your comment, in case phrases_filter
is empty, you want all rows to match, we can do this by using an if
statement:
from django.db.models import Q
from functools import reduce
from operator import or_
queryset = Phrase.objects.all()
if phrases_filter:
queryset = filter(
reduce(or_, (Q(name__contains=e) for e in phrases_filter))
)
So only in case phrases_filter
contains at least on element, we perform a filtering.