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:
filter(title__regex='^[0-9]+$') MyModel.objects.
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
=Value('test_string')).filter(val__regex=F('pattern')) MyModel.objects.alias(val
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
filter(Regex(Value('test_string'), F('pattern'))) MyModel.objects.
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.