diff --git a/backend/src/baserow/contrib/database/fields/field_filters.py b/backend/src/baserow/contrib/database/fields/field_filters.py index 3a941234b..f3441809f 100644 --- a/backend/src/baserow/contrib/database/fields/field_filters.py +++ b/backend/src/baserow/contrib/database/fields/field_filters.py @@ -137,12 +137,15 @@ class FilterBuilder: raise ValueError(f"Unknown filter type {self._filter_type}.") -def contains_filter(field_name, value, model_field, _) -> OptionallyAnnotatedQ: +def contains_filter( + field_name, value, model_field, _, validate=True +) -> OptionallyAnnotatedQ: value = value.strip() # If an empty value has been provided we do not want to filter at all. if value == "": return Q() - model_field.get_prep_value(value) + if validate: + model_field.get_prep_value(value) return Q(**{f"{field_name}__icontains": value}) diff --git a/backend/src/baserow/contrib/database/fields/field_types.py b/backend/src/baserow/contrib/database/fields/field_types.py index 765682427..1410b49ff 100755 --- a/backend/src/baserow/contrib/database/fields/field_types.py +++ b/backend/src/baserow/contrib/database/fields/field_types.py @@ -5627,7 +5627,7 @@ class UUIDFieldType(ReadOnlyFieldType): return "" if value is None else str(value) def contains_query(self, *args): - return contains_filter(*args) + return contains_filter(*args, validate=False) def to_baserow_formula_expression(self, field): # Cast the uuid to text, to make it compatible with all the text related diff --git a/backend/tests/baserow/contrib/database/view/test_view_filters.py b/backend/tests/baserow/contrib/database/view/test_view_filters.py index 1ce7391fb..97e8b72b7 100644 --- a/backend/tests/baserow/contrib/database/view/test_view_filters.py +++ b/backend/tests/baserow/contrib/database/view/test_view_filters.py @@ -5404,6 +5404,109 @@ def test_link_row_contains_filter_type_single_select_field(data_fixture): assert len(ids) == 2 +@pytest.mark.django_db +def test_link_row_contains_filter_type_uuid_field(data_fixture): + user = data_fixture.create_user() + database = data_fixture.create_database_application(user=user) + table = data_fixture.create_database_table(database=database) + related_table = data_fixture.create_database_table(database=database) + primary_field = data_fixture.create_text_field(table=table) + grid_view = data_fixture.create_grid_view(table=table) + + link_row_field = FieldHandler().create_field( + user=user, + table=table, + type_name="link_row", + name="Test", + link_row_table=related_table, + ) + + row_handler = RowHandler() + + related_primary_uuid_field = data_fixture.create_uuid_field( + primary=True, table=related_table + ) + + model = table.get_model() + related_model = related_table.get_model() + + related_uuid_row_1 = row_handler.create_row( + user=user, + table=related_table, + model=related_model, + values={}, + ) + + related_uuid_row_2 = row_handler.create_row( + user=user, + table=related_table, + model=related_model, + values={}, + ) + + row_handler.create_row( + user=user, + table=table, + model=model, + values={ + f"field_{primary_field.id}": "Row 0", + }, + ) + + row_1 = row_handler.create_row( + user=user, + table=table, + model=model, + values={ + f"field_{primary_field.id}": "Row 1", + f"field_{link_row_field.id}": [related_uuid_row_1.id], + }, + ) + + row_handler.create_row( + user=user, + table=table, + model=model, + values={ + f"field_{primary_field.id}": "Row 2", + f"field_{link_row_field.id}": [related_uuid_row_2.id], + }, + ) + + view_handler = ViewHandler() + view_filter = data_fixture.create_view_filter( + view=grid_view, + field=link_row_field, + type="link_row_contains", + value=f"", + ) + + ids = [ + r.id for r in view_handler.apply_filters(grid_view, model.objects.all()).all() + ] + assert len(ids) == 3 + + uuid_value = str( + getattr(related_uuid_row_1, f"field_{related_primary_uuid_field.id}") + ) + + view_filter.value = uuid_value[:-2] + view_filter.save() + ids = [ + r.id for r in view_handler.apply_filters(grid_view, model.objects.all()).all() + ] + assert len(ids) == 1 + assert ids == [row_1.id] + + view_filter.value = uuid_value + view_filter.save() + ids = [ + r.id for r in view_handler.apply_filters(grid_view, model.objects.all()).all() + ] + assert len(ids) == 1 + assert ids == [row_1.id] + + @pytest.mark.django_db def test_link_row_contains_filter_type_multiple_select_field(data_fixture): user = data_fixture.create_user() diff --git a/changelog/entries/unreleased/bug/2713_fix_for_uuidfieldtype_fails_hard_if_filtered_by_containsview.json b/changelog/entries/unreleased/bug/2713_fix_for_uuidfieldtype_fails_hard_if_filtered_by_containsview.json new file mode 100644 index 000000000..6112a5b11 --- /dev/null +++ b/changelog/entries/unreleased/bug/2713_fix_for_uuidfieldtype_fails_hard_if_filtered_by_containsview.json @@ -0,0 +1,7 @@ +{ + "type": "bug", + "message": "fix for UUIDFieldType that fails hard if filtered by ContainsViewFilterType and a non-UUID string", + "issue_number": 2713, + "bullet_points": [], + "created_at": "2024-06-10" +}