Skip to content

Commit 601b150

Browse files
ai field auto-update (baserow#4162)
* ai field auto-update --------- Co-authored-by: Davide Silvestri <davide@baserow.io>
1 parent e81049d commit 601b150

File tree

14 files changed

+753
-17
lines changed

14 files changed

+753
-17
lines changed

backend/src/baserow/contrib/database/fields/dependencies/update_collector.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import dataclasses
12
from collections import defaultdict
23
from typing import Dict, List, Optional, Set, Tuple, cast
34

@@ -16,6 +17,20 @@
1617
StartingRowIdsType = Optional[List[int]]
1718

1819

20+
@dataclasses.dataclass
21+
class DependencyContext:
22+
"""
23+
DependencyContext is used to pass additional dependency-related information
24+
to callbacks.
25+
"""
26+
27+
# The depth of the dependency chain from the starting
28+
# field to the field parameter. 0 means the field is a direct dependency of
29+
# the updated row's field. 1 means the field depends on a field which depends
30+
# on the updated row's field, etc.
31+
depth: int = 0
32+
33+
1934
class PathBasedUpdateStatementCollector:
2035
def __init__(
2136
self,

backend/src/baserow/contrib/database/fields/field_types.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@
175175
from .dependencies.handler import FieldDependants, FieldDependencyHandler
176176
from .dependencies.models import FieldDependency
177177
from .dependencies.types import FieldDependencies
178-
from .dependencies.update_collector import FieldUpdateCollector
178+
from .dependencies.update_collector import DependencyContext, FieldUpdateCollector
179179
from .exceptions import (
180180
AllProvidedCollaboratorIdsMustBeValidUsers,
181181
AllProvidedMultipleSelectValuesMustBeSelectOption,
@@ -3582,6 +3582,7 @@ def row_of_dependency_updated(
35823582
update_collector: FieldUpdateCollector,
35833583
field_cache: "FieldCache",
35843584
via_path_to_starting_table: List["LinkRowField"],
3585+
dependency_context: DependencyContext,
35853586
):
35863587
update_collector.add_field_which_has_changed(
35873588
field, via_path_to_starting_table, send_field_updated_signal=False
@@ -3592,6 +3593,7 @@ def row_of_dependency_updated(
35923593
update_collector,
35933594
field_cache,
35943595
via_path_to_starting_table,
3596+
dependency_context,
35953597
)
35963598

35973599
def field_dependency_updated(
@@ -5697,6 +5699,7 @@ def row_of_dependency_updated(
56975699
update_collector: FieldUpdateCollector,
56985700
field_cache: "FieldCache",
56995701
via_path_to_starting_table: Optional[List[LinkRowField]],
5702+
dependency_context: DependencyContext,
57005703
):
57015704
self._update_field_values(
57025705
field, update_collector, field_cache, via_path_to_starting_table
@@ -5708,6 +5711,7 @@ def row_of_dependency_updated(
57085711
update_collector,
57095712
field_cache,
57105713
via_path_to_starting_table,
5714+
dependency_context,
57115715
)
57125716

57135717
def _update_field_values(

backend/src/baserow/contrib/database/fields/registries.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
from baserow.contrib.database.fields.dependencies.handler import FieldDependants
8282
from baserow.contrib.database.fields.dependencies.types import FieldDependencies
8383
from baserow.contrib.database.fields.dependencies.update_collector import (
84+
DependencyContext,
8485
FieldUpdateCollector,
8586
)
8687
from baserow.contrib.database.fields.field_cache import FieldCache
@@ -1446,6 +1447,7 @@ def row_of_dependency_created(
14461447
update_collector: "FieldUpdateCollector",
14471448
field_cache: "FieldCache",
14481449
via_path_to_starting_table: Optional[List[LinkRowField]],
1450+
dependency_context: "DependencyContext",
14491451
):
14501452
"""
14511453
Called when a row is created in a dependency field (a field that the field
@@ -1469,6 +1471,7 @@ def row_of_dependency_created(
14691471
update_collector,
14701472
field_cache,
14711473
via_path_to_starting_table,
1474+
dependency_context,
14721475
)
14731476

14741477
def row_of_dependency_updated(
@@ -1478,6 +1481,7 @@ def row_of_dependency_updated(
14781481
update_collector: "FieldUpdateCollector",
14791482
field_cache: "FieldCache",
14801483
via_path_to_starting_table: List["LinkRowField"],
1484+
dependency_context: "DependencyContext",
14811485
):
14821486
"""
14831487
Called when a row or rows are updated in a dependency field (a field that the
@@ -1495,6 +1499,8 @@ def row_of_dependency_updated(
14951499
:param field_cache: An optional field cache to be used when fetching fields.
14961500
:param via_path_to_starting_table: A list of link row fields if any leading
14971501
back to the starting table where the first row was changed.
1502+
:param dependency_context: A DependencyContext object containing additional
1503+
information about the dependency and the triggering change.
14981504
"""
14991505

15001506
def row_of_dependency_deleted(
@@ -1504,6 +1510,7 @@ def row_of_dependency_deleted(
15041510
update_collector: "FieldUpdateCollector",
15051511
field_cache: "FieldCache",
15061512
via_path_to_starting_table: Optional[List[LinkRowField]],
1513+
dependency_context: "DependencyContext",
15071514
):
15081515
"""
15091516
Called when a row is deleted in a dependency field (a field that the
@@ -1527,6 +1534,7 @@ def row_of_dependency_deleted(
15271534
update_collector,
15281535
field_cache,
15291536
via_path_to_starting_table,
1537+
dependency_context,
15301538
)
15311539

15321540
def field_dependency_created(

backend/src/baserow/contrib/database/rows/handler.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from baserow.contrib.database.field_rules.handlers import FieldRuleHandler
3535
from baserow.contrib.database.fields.dependencies.handler import FieldDependencyHandler
3636
from baserow.contrib.database.fields.dependencies.update_collector import (
37+
DependencyContext,
3738
FieldUpdateCollector,
3839
)
3940
from baserow.contrib.database.fields.exceptions import (
@@ -1154,7 +1155,10 @@ def update_dependencies_of_rows_updated(
11541155
deleted_m2m_rels_per_link_field=deleted_m2m_rels_per_link_field,
11551156
)
11561157
updated_fields = []
1157-
for dependant_fields_group in all_dependent_fields_grouped_by_depth:
1158+
for depth, dependant_fields_group in enumerate(
1159+
all_dependent_fields_grouped_by_depth
1160+
):
1161+
dependency_context = DependencyContext(depth=depth)
11581162
for (
11591163
dependant_field,
11601164
dependant_field_type,
@@ -1167,6 +1171,7 @@ def update_dependencies_of_rows_updated(
11671171
update_collector,
11681172
field_cache,
11691173
path_to_starting_table,
1174+
dependency_context,
11701175
)
11711176
update_collector.apply_updates_and_get_updated_fields(
11721177
field_cache, skip_search_updates
@@ -1486,19 +1491,24 @@ def update_dependencies_of_rows_created(
14861491
)
14871492
)
14881493

1489-
for dependant_fields_group in all_dependent_fields_grouped_by_depth:
1494+
for depth, dependant_fields_group in enumerate(
1495+
all_dependent_fields_grouped_by_depth
1496+
):
1497+
dependency_context = DependencyContext(depth=depth)
14901498
for (
14911499
dependant_field,
14921500
dependant_field_type,
14931501
path_to_starting_table,
14941502
) in dependant_fields_group:
14951503
dependant_fields.append(dependant_field)
1504+
14961505
dependant_field_type.row_of_dependency_created(
14971506
dependant_field,
14981507
created_rows,
14991508
update_collector,
15001509
field_cache,
15011510
path_to_starting_table,
1511+
dependency_context,
15021512
)
15031513
update_collector.apply_updates_and_get_updated_fields(field_cache)
15041514
return fields, dependant_fields
@@ -2754,7 +2764,10 @@ def update_dependencies_of_rows_deleted(self, table, row, model):
27542764
)
27552765
)
27562766

2757-
for dependent_fields_level in all_dependent_fields_grouped_by_level:
2767+
for depth, dependent_fields_level in enumerate(
2768+
all_dependent_fields_grouped_by_level
2769+
):
2770+
dependency_context = DependencyContext(depth=depth)
27582771
for (
27592772
dependant_field,
27602773
dependant_field_type,
@@ -2768,6 +2781,7 @@ def update_dependencies_of_rows_deleted(self, table, row, model):
27682781
update_collector,
27692782
field_cache,
27702783
path_to_starting_table,
2784+
dependency_context,
27712785
)
27722786

27732787
update_collector.apply_updates_and_get_updated_fields(field_cache)
@@ -2910,7 +2924,10 @@ def force_delete_rows(
29102924
)
29112925
)
29122926

2913-
for dependent_fields_level in all_dependent_fields_grouped_by_level:
2927+
for depth, dependent_fields_level in enumerate(
2928+
all_dependent_fields_grouped_by_level
2929+
):
2930+
dependency_context = DependencyContext(depth=depth)
29142931
for (
29152932
table_id,
29162933
dependant_field,
@@ -2924,6 +2941,7 @@ def force_delete_rows(
29242941
update_collector,
29252942
field_cache,
29262943
path_to_starting_table,
2944+
dependency_context,
29272945
)
29282946
update_collector.apply_updates_and_get_updated_fields(field_cache)
29292947

backend/tests/baserow/contrib/database/field/test_formula_field_type.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT
1313

1414
from baserow.contrib.database.fields.dependencies.update_collector import (
15+
DependencyContext,
1516
FieldUpdateCollector,
1617
)
1718
from baserow.contrib.database.fields.field_cache import FieldCache
@@ -864,23 +865,24 @@ def test_row_dependency_update_functions_do_one_row_updates_for_same_table(
864865
field_cache = FieldCache()
865866
field_cache.cache_model(table_model)
866867

868+
dependency_context = DependencyContext(depth=0)
867869
formula_field_type.row_of_dependency_updated(
868-
formula_field, row, update_collector, field_cache, None
870+
formula_field, row, update_collector, field_cache, None, dependency_context
869871
)
870872
formula_field_type.row_of_dependency_updated(
871-
formula_field, row, update_collector, field_cache, []
873+
formula_field, row, update_collector, field_cache, [], dependency_context
872874
)
873875
formula_field_type.row_of_dependency_created(
874-
formula_field, row, update_collector, field_cache, None
876+
formula_field, row, update_collector, field_cache, None, dependency_context
875877
)
876878
formula_field_type.row_of_dependency_created(
877-
formula_field, row, update_collector, field_cache, []
879+
formula_field, row, update_collector, field_cache, [], dependency_context
878880
)
879881
formula_field_type.row_of_dependency_deleted(
880-
formula_field, row, update_collector, field_cache, None
882+
formula_field, row, update_collector, field_cache, None, dependency_context
881883
)
882884
formula_field_type.row_of_dependency_deleted(
883-
formula_field, row, update_collector, field_cache, []
885+
formula_field, row, update_collector, field_cache, [], dependency_context
884886
)
885887
# Does one update to update the last_system_or_user_row_update_on column
886888
with django_assert_num_queries(1):
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type": "feature",
3+
"message": "AI field auto-update",
4+
"domain": "database",
5+
"issue_number": 4115,
6+
"bullet_points": [],
7+
"created_at": "2025-11-04"
8+
}

0 commit comments

Comments
 (0)