Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions backend/src/baserow/contrib/database/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,14 +477,14 @@ def ready(self):
HasNotValueHigherThanFilterType,
HasNotValueLowerOrEqualTHanFilterType,
HasNotValueLowerThanFilterType,
HasValueComparableToFilter,
HasValueContainsViewFilterType,
HasValueContainsWordViewFilterType,
HasValueEqualViewFilterType,
HasValueHigherOrEqualThanFilter,
HasValueLengthIsLowerThanViewFilterType,
HasValueLowerOrEqualThanFilter,
HasValueLowerThanFilter,
hasValueComparableToFilter,
)

view_filter_type_registry.register(HasValueEqualViewFilterType())
Expand All @@ -501,7 +501,7 @@ def ready(self):
view_filter_type_registry.register(HasNoneSelectOptionEqualViewFilterType())
view_filter_type_registry.register(HasValueLowerThanFilter())
view_filter_type_registry.register(HasValueLowerOrEqualThanFilter())
view_filter_type_registry.register(hasValueComparableToFilter())
view_filter_type_registry.register(HasValueComparableToFilter())
view_filter_type_registry.register(HasValueHigherOrEqualThanFilter())
view_filter_type_registry.register(HasNotValueHigherOrEqualTHanFilterType())
view_filter_type_registry.register(HasNotValueHigherThanFilterType())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,3 +269,42 @@ def get_template_data(self, sql_value) -> dict:
data = super().get_template_data(sql_value)
data["comparison_op"] = self.comparison_op.value
return data


class JSONArrayCompareIntervalValueExpr(BaserowFilterExpression):
"""
Base class for expressions that compare an interval value in a JSON array.
Together with the field_name and value, a comparison operator must be provided to be
used in the template.
"""

def __init__(
self,
field_name: F,
value: Value,
comparison_op: ComparisonOperator,
output_field: Field,
):
super().__init__(field_name, value, output_field)
if not isinstance(comparison_op, ComparisonOperator):
raise ValueError(
f"comparison_op must be a ComparisonOperator, not {type(comparison_op)}"
)
self.comparison_op = comparison_op

# fmt: off
template = (
f"""
EXISTS(
SELECT 1
FROM JSONB_ARRAY_ELEMENTS(%(field_name)s) as filtered_field
WHERE (filtered_field ->> 'value')::interval %(comparison_op)s make_interval(secs=>%(value)s)
)
""" # nosec B608 %(value)s %(comparison_op)s
)
# fmt: on

def get_template_data(self, sql_value) -> dict:
data = super().get_template_data(sql_value)
data["comparison_op"] = self.comparison_op.value
return data
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
)
from baserow.contrib.database.formula.expression_generator.django_expressions import (
ComparisonOperator,
JSONArrayCompareIntervalValueExpr,
JSONArrayCompareNumericValueExpr,
)
from baserow.contrib.database.formula.registries import formula_function_registry
Expand Down Expand Up @@ -813,6 +814,31 @@ def get_order_by_in_array_expr(self, field, field_name, order_direction):
field_name, "value", "interval", output_field=models.DurationField()
)

def get_has_numeric_value_comparable_to_filter_query(
self,
field_name: str,
value: str,
model_field: models.Field,
field: "Field",
comparison_op: ComparisonOperator,
) -> "OptionallyAnnotatedQ":
try:
value = int(value)
except (TypeError, ValueError):
return Q()

return get_array_json_filter_expression(
JSONArrayCompareIntervalValueExpr,
field_name,
Value(value),
comparison_op=comparison_op,
)

def get_in_array_is_query(self, field_name, value, model_field, field):
return self.get_has_numeric_value_comparable_to_filter_query(
field_name, value, model_field, field, ComparisonOperator.EQUAL
)


class BaserowFormulaDateType(
HasValueEmptyFilterSupport, HasValueContainsFilterSupport, BaserowFormulaValidType
Expand Down
4 changes: 2 additions & 2 deletions backend/src/baserow/contrib/database/rows/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1808,8 +1808,8 @@ def import_rows(
and not field_object["field"].read_only
]

# Sort by order then by id
fields.sort(key=lambda f: (f.order, f.id))
# Sort by primary first (descending), then by order, then by id
fields.sort(key=lambda f: (not f.primary, f.order, f.id))

for index, row in enumerate(data):
# Check row length
Expand Down
2 changes: 2 additions & 0 deletions backend/src/baserow/contrib/database/table/queryset.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def _as_sql():

def execute_sql(self, result_type):
cursor = super(SQLUpdateCompiler, self).execute_sql(result_type)
if cursor is None:
return []
return [res[0] for res in cursor.fetchall()]


Expand Down
19 changes: 13 additions & 6 deletions backend/src/baserow/contrib/database/views/array_view_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
BaserowFormulaBooleanType,
BaserowFormulaCharType,
BaserowFormulaDateType,
BaserowFormulaDurationType,
BaserowFormulaMultipleSelectType,
BaserowFormulaSingleSelectType,
BaserowFormulaURLType,
Expand All @@ -55,6 +56,7 @@ class HasEmptyValueViewFilterType(ViewFilterType):
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
FormulaFieldType.array_of(BaserowFormulaMultipleSelectType.type),
FormulaFieldType.array_of(BaserowFormulaMultipleCollaboratorsType.type),
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
),
]

Expand Down Expand Up @@ -124,6 +126,7 @@ class HasValueEqualViewFilterType(ComparisonHasValueFilter):
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
FormulaFieldType.array_of(BaserowFormulaMultipleSelectType.type),
FormulaFieldType.array_of(BaserowFormulaMultipleCollaboratorsType.type),
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
),
]

Expand Down Expand Up @@ -287,11 +290,12 @@ class HasNoneSelectOptionEqualViewFilterType(
type = "has_none_select_option_equal"


class hasValueComparableToFilter(ComparisonHasValueFilter):
class HasValueComparableToFilter(ComparisonHasValueFilter):
type = "has_value_higher"
compatible_field_types = [
FormulaFieldType.compatible_with_formula_types(
FormulaFieldType.array_of(BaserowFormulaNumberType.type)
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
),
]

Expand All @@ -305,7 +309,7 @@ def get_filter_expression(self, field_name, value, model_field, field):


class HasNotValueHigherThanFilterType(
NotViewFilterTypeMixin, hasValueComparableToFilter
NotViewFilterTypeMixin, HasValueComparableToFilter
):
type = "has_not_value_higher"

Expand All @@ -314,7 +318,8 @@ class HasValueHigherOrEqualThanFilter(ComparisonHasValueFilter):
type = "has_value_higher_or_equal"
compatible_field_types = [
FormulaFieldType.compatible_with_formula_types(
FormulaFieldType.array_of(BaserowFormulaNumberType.type)
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
),
]

Expand All @@ -341,7 +346,8 @@ class HasValueLowerThanFilter(ComparisonHasValueFilter):
type = "has_value_lower"
compatible_field_types = [
FormulaFieldType.compatible_with_formula_types(
FormulaFieldType.array_of(BaserowFormulaNumberType.type)
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
),
]

Expand All @@ -362,7 +368,8 @@ class HasValueLowerOrEqualThanFilter(ComparisonHasValueFilter):
type = "has_value_lower_or_equal"
compatible_field_types = [
FormulaFieldType.compatible_with_formula_types(
FormulaFieldType.array_of(BaserowFormulaNumberType.type)
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
),
]

Expand Down
58 changes: 58 additions & 0 deletions backend/tests/baserow/contrib/database/rows/test_rows_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1313,3 +1313,61 @@ def test_can_undo_redo_update_rows_interesting_field_types(data_fixture):
)
) == [multi_select_option_2.id]
assert getattr(row_table_1, f"field_{formula_field.id}") == "New value"


@pytest.mark.django_db(transaction=True)
def test_import_rows_respects_primary_priority_sorting(data_fixture):
user = data_fixture.create_user()
database = data_fixture.create_database_application(
user=user, name="Sample database"
)
table = data_fixture.create_database_table(database=database, name="Sample table")

single_select = data_fixture.create_single_select_field(
table=table, name="Single select", order=4, primary=False
)
data_fixture.create_select_option(
field=single_select, value="A", color="dark-green", order=0
)
data_fixture.create_select_option(
field=single_select, value="B", color="light-blue", order=1
)

name_field = data_fixture.create_text_field(
table=table, name="Name", order=5, primary=True
)

text_field = data_fixture.create_text_field(
table=table, name="Text", order=8, primary=False
)

data_fixture.create_formula_field(
table=table,
name="Formula",
order=58,
formula="field('Text')",
formula_type="text",
internal_formula="field('Text')",
nullable=True,
recalculate=True,
)

data = [["N", "A", "text"]]

created_rows, error_report = ImportRowsActionType.do(
user,
table,
data={"data": data, "configuration": None},
progress=None,
)

assert error_report == {}
assert len(created_rows) == 1

row = created_rows[0]
model = table.get_model()
stored = model.objects.get(id=row.id)

assert getattr(stored, name_field.db_column) == "N"
assert getattr(stored, text_field.db_column) == "text"
assert getattr(stored, single_select.db_column).value == "A"
Loading
Loading