(core) Two way reference polish

Summary:
- Fixing BulkRemoveRecord bug
- Rewriting copy on the `delete reverse column` dialog
- Rewriting text on the `reassign reference dialog`
- Adding tooltip that explains why 2-way references are not enabled for formula columns

Test Plan: Added tests

Reviewers: georgegevoian

Reviewed By: georgegevoian

Subscribers: paulfitz

Differential Revision: https://phab.getgrist.com/D4355
This commit is contained in:
Jarosław Sadziński
2024-09-23 17:52:11 +02:00
parent 8b1d1c5d25
commit 8da04d5a2a
8 changed files with 179 additions and 44 deletions

View File

@@ -20,6 +20,9 @@ Bob = 2
Penny = 3
EmptyList = None
def uniqueReferences(rec):
return rec.reverseCol and rec.reverseCol.type.startswith('Ref:')
class TestTwoWayReferences(test_engine.EngineTestCase):
def get_col_rec(self, tableId, colId):
@@ -319,6 +322,16 @@ class TestTwoWayReferences(test_engine.EngineTestCase):
# Add a pet named Rex with Bob as owner
self.apply_user_action(["AddRecord", "Pets", 1, {"Name": "Rex", "Owner": Bob}])
self.assertTableData("Owners", cols="subset", data=[
["id", "Name"],
[Alice, "Alice"],
[Bob, "Bob"],
])
self.assertTableData("Pets", cols="subset", data=[
["id", "Name", "Owner"],
[Rex, "Rex", Bob],
])
def test_uniques(self):
self.load_pets()
@@ -1192,9 +1205,78 @@ class TestTwoWayReferences(test_engine.EngineTestCase):
[Rex, "Rex", Alice],
])
def test_back_loop(self):
"""
Test that updating reverse column doesn't cause infinite loop.
"""
def uniqueReferences(rec):
return rec.reverseCol and rec.reverseCol.type.startswith('Ref:')
# Load pets sample.
self.load_pets()
# Add reverse column for Owner.
self.apply_user_action(["AddReverseColumn", 'Pets', 'Owner'])
# Convert Pets to Ref:Owners.
self.apply_user_action(["ModifyColumn", "Owners", "Pets", {"type": "Ref:Pets"}])
# Check the data.
self.assertTableData("Pets", cols="subset", data=[
["id", "Name", "Owner"],
[1, "Rex", Bob],
])
self.assertTableData("Owners", cols="subset", data=[
["id", "Name", "Pets"],
[1, "Alice", Empty],
[2, "Bob", Rex],
])
# Now move Rex to Alice using Pets table.
self.apply_user_action(["UpdateRecord", "Pets", Rex, {"Owner": Alice}])
def test_remove_in_bulk(self):
"""
Test that we can remove many rows at the same time. PReviously it ended up in an error,
as the reverse column was trying to update the removed row.
"""
# Load pets sample.
self.load_pets()
# Add another dog.
self.apply_user_action(["AddRecord", "Pets", Pluto, {"Name": "Pluto"}])
# Add reverse column for Owner.
self.apply_user_action(["AddReverseColumn", 'Pets', 'Owner'])
# Add Pluto to Bob.
self.apply_user_action(["UpdateRecord", "Pets", Pluto, {"Owner": Alice}])
# Test the data.
self.assertTableData("Pets", cols="subset", data=[
["id", "Name", "Owner"],
[Rex, "Rex", Bob],
[Pluto, "Pluto", Alice],
])
self.assertTableData("Owners", cols="subset", data=[
["id", "Name", "Pets"],
[1, "Alice", [Pluto]],
[2, "Bob", [Rex]],
])
# Now remove both dogs.
self.apply_user_action(["BulkRemoveRecord", "Pets", [Rex, Pluto]])
# Make sure we see the data.
self.assertTableData("Pets", cols="subset", data=[
["id", "Name", "Owner"],
])
self.assertTableData("Owners", cols="subset", data=[
["id", "Name", "Pets"],
[1, "Alice", EmptyList],
[2, "Bob", EmptyList],
])
if __name__ == "__main__":

View File

@@ -1164,13 +1164,19 @@ class UserActions(object):
continue
updates = ref_col.get_updates_for_removed_target_rows(row_id_set)
if updates:
# Previously we sent this as a docaction. Now we do a proper useraction with all the
# processing that involves, e.g. triggering two-way-reference updates, and also all the
# metadata checks and updates.
self._BulkUpdateRecord_decoded(ref_col.table_id,
[row_id for (row_id, value) in updates],
{ ref_col.col_id: [value for (row_id, value) in updates] }
)
table_id = ref_col.table_id
rows = [row_id for (row_id, value) in updates]
columns = {ref_col.col_id: [value for (row_id, value) in updates]}
if ref_col.table_id.startswith('_grist_'):
# Previously we sent this as a docaction. Now we do a proper useraction with all the
# processing that involves, e.g. triggering two-way-reference updates, and also all the
# metadata checks and updates.
self._BulkUpdateRecord_decoded(table_id, rows, columns)
else:
# But for normal user tables (with two-way references), we must still use the docaction,
# otherwise we'd invoke two-way update logic, and the reverse column would try to update
# rows that we just deleted.
self._do_doc_action(actions.BulkUpdateRecord(table_id, rows, columns))
@useraction
def RemoveRecord(self, table_id, row_id):