mirror of
https://gitlab.com/bramw/baserow.git
synced 2025-03-14 20:52:51 +00:00
Call ViewHandler().field_type_changed() even if the db field representation changes
This commit is contained in:
parent
bf816f5c61
commit
2ca124e802
6 changed files with 103 additions and 5 deletions
backend
src/baserow/contrib/database/fields
tests/baserow/contrib/database/field
changelog/entries/1.16.0/bug
|
@ -3168,6 +3168,13 @@ class FormulaFieldType(ReadOnlyFieldType):
|
|||
**kwargs,
|
||||
)
|
||||
|
||||
def has_compatible_model_fields(self, instance, instance2) -> bool:
|
||||
return (
|
||||
super().has_compatible_model_fields(instance, instance2)
|
||||
and instance.formula_type == instance2.formula_type
|
||||
and instance.array_formula_type == instance.array_formula_type
|
||||
)
|
||||
|
||||
def prepare_value_for_db(self, instance, value):
|
||||
"""
|
||||
Since the Formula Field is a read only field, we raise a
|
||||
|
|
|
@ -413,8 +413,7 @@ class FieldHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
to_field_type_name = new_type_name or from_field_type.type
|
||||
|
||||
# If the provided field type does not match with the current one we need to
|
||||
# migrate the field to the new type. Because the type has changed we also need
|
||||
# to remove all view filters.
|
||||
# migrate the field to the new type.
|
||||
baserow_field_type_changed = from_field_type.type != to_field_type_name
|
||||
field_cache = FieldCache()
|
||||
if baserow_field_type_changed:
|
||||
|
@ -431,9 +430,6 @@ class FieldHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
new_model_class = to_field_type.model_class
|
||||
field.change_polymorphic_type_to(new_model_class)
|
||||
|
||||
# If the field type changes it could be that some dependencies,
|
||||
# like filters or sortings need to be changed.
|
||||
ViewHandler().field_type_changed(field)
|
||||
else:
|
||||
dependants_broken_due_to_type_change = []
|
||||
to_field_type = from_field_type
|
||||
|
@ -459,6 +455,16 @@ class FieldHandler(metaclass=baserow_trace_methods(tracer)):
|
|||
from_model_field = from_model._meta.get_field(field.db_column)
|
||||
to_model_field = to_model._meta.get_field(field.db_column)
|
||||
|
||||
# If the field type or the database representation changes it could be
|
||||
# that some view dependencies like filters or sortings need to be changed.
|
||||
if (
|
||||
baserow_field_type_changed
|
||||
or not from_field_type.has_compatible_model_fields(
|
||||
from_model_field, to_model_field
|
||||
)
|
||||
):
|
||||
ViewHandler().field_type_changed(field)
|
||||
|
||||
# Before a field is updated we are going to call the before_schema_change
|
||||
# method of the old field because some cleanup of related instances might
|
||||
# need to happen.
|
||||
|
|
|
@ -354,6 +354,13 @@ class FieldType(
|
|||
|
||||
raise NotImplementedError("Each must have his own get_model_field method.")
|
||||
|
||||
def has_compatible_model_fields(self, instance, instance2) -> bool:
|
||||
"""
|
||||
Returns True if the provided instances have compatible model fields.
|
||||
"""
|
||||
|
||||
return type(instance) == type(instance2)
|
||||
|
||||
def after_model_generation(self, instance, model, field_name, manytomany_models):
|
||||
"""
|
||||
After the model is generated the after_model_generation method of each field
|
||||
|
|
|
@ -40,6 +40,7 @@ from baserow.contrib.database.fields.models import (
|
|||
from baserow.contrib.database.fields.registries import field_type_registry
|
||||
from baserow.contrib.database.rows.handler import RowHandler
|
||||
from baserow.contrib.database.table.models import Table
|
||||
from baserow.contrib.database.views.models import ViewFilter
|
||||
from baserow.core.exceptions import UserNotInWorkspace
|
||||
|
||||
# You must add --run-disabled-in-ci to pytest to run this test, you can do this in
|
||||
|
@ -480,6 +481,43 @@ def test_update_field(send_mock, data_fixture):
|
|||
assert getattr(field_with_max_length_name, "name") == field_name_with_ok_length
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.field_formula
|
||||
@patch("baserow.contrib.database.fields.signals.field_updated.send")
|
||||
def test_update_field_reset_filter_formula_field(send_mock, data_fixture):
|
||||
"""
|
||||
When there is a field change changing the underlying database
|
||||
type of the field, the update_field() method should initiate
|
||||
a cleanup for affected views. Formula fields can be in this
|
||||
situation when the formula expression changes.
|
||||
"""
|
||||
|
||||
user = data_fixture.create_user()
|
||||
table = data_fixture.create_database_table(user=user)
|
||||
field = data_fixture.create_formula_field(table=table, order=1, formula="today()")
|
||||
view = data_fixture.create_grid_view(table=table)
|
||||
# create a view filter that won't be compatible with the new type
|
||||
data_fixture.create_view_filter(
|
||||
view=view, field=field, type="date_equals_today", value="UTC?"
|
||||
)
|
||||
model = table.get_model()
|
||||
model.objects.create()
|
||||
handler = FieldHandler()
|
||||
|
||||
field = handler.update_field(
|
||||
user=user,
|
||||
field=field,
|
||||
new_type_name="formula",
|
||||
formula="1",
|
||||
)
|
||||
|
||||
field.refresh_from_db()
|
||||
rows = model.objects.all()
|
||||
assert getattr(rows[0], f"field_{field.id}") == Decimal("1")
|
||||
# incompatible filter has been removed
|
||||
assert ViewFilter.objects.filter(view=view).count() == 0
|
||||
|
||||
|
||||
# This failing field type triggers the CannotChangeFieldType error if a field is
|
||||
# changed into this type.
|
||||
class FailingFieldType(TextFieldType):
|
||||
|
|
|
@ -1507,3 +1507,36 @@ def test_user_can_change_date_force_timezone_on_formula(data_fixture, api_client
|
|||
}
|
||||
actual = {key: value for key, value in response.json().items() if key in expected}
|
||||
assert actual == expected
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.field_formula
|
||||
@pytest.mark.parametrize(
|
||||
"instance1,instance2,is_compatible",
|
||||
[
|
||||
(
|
||||
FormulaField(formula_type="date", array_formula_type=None),
|
||||
FormulaField(formula_type="date", array_formula_type=None),
|
||||
True,
|
||||
),
|
||||
(
|
||||
FormulaField(formula_type="array", array_formula_type="number"),
|
||||
FormulaField(formula_type="array", array_formula_type="number"),
|
||||
True,
|
||||
),
|
||||
(
|
||||
FormulaField(formula_type="date", array_formula_type=None),
|
||||
FormulaField(formula_type="number", array_formula_type=None),
|
||||
False,
|
||||
),
|
||||
(
|
||||
FormulaField(formula_type="array", array_formula_type="number"),
|
||||
FormulaField(formula_type="array", array_formula_type="text"),
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_has_compatible_model_fields(instance1, instance2, is_compatible):
|
||||
FormulaFieldType().has_compatible_model_fields(
|
||||
instance1, instance2
|
||||
) is is_compatible
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "bug",
|
||||
"message": "Fix Updating formula field doesn't delete incompatible filters and sorts",
|
||||
"issue_number": 1608,
|
||||
"bullet_points": [],
|
||||
"created_at": "2023-03-31"
|
||||
}
|
Loading…
Reference in a new issue