What problems are solved with this?
Django's ORM is quite expressive, and lookups can be used to filter in a specific way, for example:
MyModel.objects.filter(title__regex='^[0-9]+$')
will search for MyModel
objects where
the title satisfies a certain regex. Sometimes we
however might want to do the opposite: a model might
contain a lot of regex patterns, we are given a string
to satisfy these, and then want to find the
MyModel
s that have a pattern that contains
this regex.
At the moment of writing there is no reverse lookup:
we can not perform a
MyModel.objects.filter(pattern__reverse_regex='test_string')
where pattern
is a field in
MyModel
that contains the regex, and we
want to look for MyModel
s where the
pattern
accepts in this case
'test_string'
as a string for this
pattern.
What does this pattern look like?
A simple, but a bit "ugly" way to solve this
is by using .alias(…)
[Django-doc]
to "inject" the value as a field in the queryset and
then thus query with that "field", we can then use an F
expression [Django-doc] to refer to the
pattern
field. This thus then looks
like:
from django.db.models import F, Value
MyModel.objects.alias(val=Value('test_string')).filter(val__regex=F('pattern'))
Since we use .alias(…)
the value will
not appear in the SELECT
clause, which is a
good thing, but it looks a bit "hacky".
But probably a more robust, and clearer way to show
what we are doing, is building a query object like
Django does behind the curtains when we perform lookups.
Indeed, if we write pattern__regex
, it
makes a lookup for a field named pattern
,
and inspects what type of field it is. For a
CharField
a different set of lookups will
be "registered". If we then write __regex
,
it will start looking for a lookup registered at this
field with the name regex
. These lookups
typically reside in the
django.db.models.lookups
module. For this
specific case, this is the Regex
class. We
thus can construct such query with:
from django.db.models import F, Value
from django.db.models.lookups import Regex
MyModel.objects.filter(Regex(Value('test_string'), F('pattern')))
This is equivalent to the query above, but makes it
clear we want to filter based on a regex, where
Value('test_string')
is the string we want
to test, and the field pattern
(F('pattern')
) is the regex we will use to
test the string.