diff --git a/awx/api/filters.py b/awx/api/filters.py index 90f499d671..e296019e75 100644 --- a/awx/api/filters.py +++ b/awx/api/filters.py @@ -232,6 +232,9 @@ class FieldLookupBackend(BaseFilterBackend): re.compile(value) except re.error as e: raise ValueError(e.args[0]) + elif new_lookup.endswith('__iexact'): + if not isinstance(field, (CharField, TextField)): + raise ValueError(f'{field.name} is not a text field and cannot be filtered by case-insensitive search') elif new_lookup.endswith('__search'): related_model = getattr(field, 'related_model', None) if not related_model: @@ -258,8 +261,8 @@ class FieldLookupBackend(BaseFilterBackend): search_filters = {} needs_distinct = False # Can only have two values: 'AND', 'OR' - # If 'AND' is used, an iterm must satisfy all condition to show up in the results. - # If 'OR' is used, an item just need to satisfy one condition to appear in results. + # If 'AND' is used, an item must satisfy all conditions to show up in the results. + # If 'OR' is used, an item just needs to satisfy one condition to appear in results. search_filter_relation = 'OR' for key, values in request.query_params.lists(): if key in self.RESERVED_NAMES: diff --git a/awx/main/tests/unit/api/test_filters.py b/awx/main/tests/unit/api/test_filters.py index 21e651e22b..7d6501a871 100644 --- a/awx/main/tests/unit/api/test_filters.py +++ b/awx/main/tests/unit/api/test_filters.py @@ -79,6 +79,19 @@ def test_invalid_field(): assert 'is not an allowed field name. Must be ascii encodable.' in str(excinfo.value) +def test_valid_iexact(): + field_lookup = FieldLookupBackend() + value, new_lookup, _ = field_lookup.value_to_python(JobTemplate, 'project__name__iexact', 'foo') + assert 'foo' in value + + +def test_invalid_iexact(): + field_lookup = FieldLookupBackend() + with pytest.raises(ValueError) as excinfo: + field_lookup.value_to_python(Job, 'id__iexact', '1') + assert 'is not a text field and cannot be filtered by case-insensitive search' in str(excinfo.value) + + @pytest.mark.parametrize('lookup_suffix', ['', 'contains', 'startswith', 'in']) @pytest.mark.parametrize('password_field', Credential.PASSWORD_FIELDS) def test_filter_on_password_field(password_field, lookup_suffix):