Advanced filters can be resolved by Q () and query expressions such as Func (), Value (), and F (). The only trick used is the rhs_only Custom Search , which uses the right side of the search and ignores the left side, because itβs easier to use all concatenated fields right on the right side. The unforgettable function concat_like encapsulates everything that is easy to use in queries.
from django.db.models import F, Func, Lookup, Q, Value from django.db.models.fields import Field def concat_like(columns, pattern): """Lookup filter: CONCAT_WS(' ', column_0, column_1...) LIKE pattern""" lhs = '%s__rhs_only' % columns[0] expr = Func(*(F(x) for x in columns), template="CONCAT_WS(' ', %(expressions)s)") return Q(**{lhs: Like(expr, Value(pattern))}) class Like(Func): def as_sql(self, compiler, connection): arg_sql, arg_params = zip(*[compiler.compile(x) for x in self.source_expressions]) return ("%s LIKE '%s'" % tuple(arg_sql)), arg_params[0] + arg_params[1] @Field.register_lookup class RhsOnly(Lookup): """Skip the LHS and evaluate the boolean RHS only""" lookup_name = 'rhs_only' def as_sql(self, compiler, connection): return self.process_rhs(compiler, connection)
All boolean expressions and related objects are supported by this code. All arguments are properly escaped.
Usage example:
>>> qs = MyModel.objects.filter(Q(id=1) | concat_like(('first_name', 'surname'), 'searched')) >>> str(qs.query)
hynekcer
source share