|
| 1 | +% querying in the opposite direction |
| 2 | +--- |
| 3 | +type: pattern |
| 4 | +typefa: "fas fa-shapes" |
| 5 | +tags: [orm, query, lookup] |
| 6 | +layers: [orm, views, models] |
| 7 | +related_packages: [] |
| 8 | +solinks: ["https://stackoverflow.com/questions/78132711/i-need-a-django-filter-query-that-takes-a-string-as-argument-and-it-gets-matched"] |
| 9 | +--- |
| 10 | + |
| 11 | +# What problems are solved with this? |
| 12 | + |
| 13 | + |
| 14 | +Django's ORM is quite expressive, and lookups can be used to filter in a specific way, for example: |
| 15 | + |
| 16 | +``` |
| 17 | +MyModel.objects.filter(title__regex='^[0-9]+$') |
| 18 | +``` |
| 19 | + |
| 20 | +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. |
| 21 | + |
| 22 | +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. |
| 23 | + |
| 24 | +# What does this pattern look like? |
| 25 | + |
| 26 | +A simple, but a bit "*ugly*" way to solve this is by using [**<code>.alias(…)</code>** <sup>\[Django-doc\]</sup>](https://docs.djangoproject.com/en/stable/ref/models/querysets/#alias) to "inject" the value as a field in the queryset and then thus query with that "field", we can then use an [**`F`** expression <sup>\[Django-doc\]](https://docs.djangoproject.com/en/stable/ref/models/expressions/#django.db.models.F) to refer to the `pattern` field. This thus then looks like: |
| 27 | + |
| 28 | +``` |
| 29 | +from django.db.models import F, Value |
| 30 | +
|
| 31 | +MyModel.objects.alias(val=Value('test_string')).filter(val__regex=F('pattern')) |
| 32 | +``` |
| 33 | + |
| 34 | +Since we use <code>.alias(…)</code> the value will not appear in the `SELECT` clause, which is a good thing, but it looks a bit "*hacky*". |
| 35 | + |
| 36 | +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: |
| 37 | + |
| 38 | +``` |
| 39 | +from django.db.models import F, Value |
| 40 | +from django.db.models.lookups import Regex |
| 41 | +
|
| 42 | +MyModel.objects.filter(Regex(Value('test_string'), F('pattern'))) |
| 43 | +``` |
| 44 | + |
| 45 | +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. |
0 commit comments