You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gristlabs_grist-core/sandbox/grist/reverse_references.py

74 lines
3.0 KiB

from collections import defaultdict
import six
from usertypes import get_referenced_table_id
class _RefUpdates(object):
def __init__(self):
self.removals = set()
self.additions = set()
def get_reverse_adjustments(row_ids, old_values, new_values, value_iterator, relation):
"""
Generates data for updating reverse columns, based on changes to this column
"""
# Stores removals and addons for each target row
affected_target_rows = defaultdict(_RefUpdates)
# Iterate over changes to source column (my column)
for (source_row_id, old_value, new_value) in zip(row_ids, old_values, new_values):
if new_value != old_value:
# Treat old_values as removals, and new_values as additions
for target_row_id in value_iterator(old_value):
affected_target_rows[target_row_id].removals.add(source_row_id)
for target_row_id in value_iterator(new_value):
affected_target_rows[target_row_id].additions.add(source_row_id)
# Now in affected_target_rows, we have the changes (deltas), now we are going to convert them
# to updates (full list of values) in target columns.
adjustments = []
# For each target row (that needs to be updated, and was change in our column)
for target_row_id, updates in six.iteritems(affected_target_rows):
# Get the value stored in that column by using our own relation object (which should store
# correct values - the same that are stored in that reverse column). `reverse_value` is the
# value in that reverse cell
reverse_value = relation.get_affected_rows((target_row_id,))
# Now make the adjustments using calculated deltas
for source_row_id in updates.removals:
reverse_value.discard(source_row_id)
for source_row_id in updates.additions:
reverse_value.add(source_row_id)
adjustments.append((target_row_id, sorted(reverse_value)))
return adjustments
def check_desired_reverse_col(col_type, desired_reverse_col):
if not desired_reverse_col:
raise ValueError("invalid column specified in reverseCol")
if desired_reverse_col.reverseCol:
raise ValueError("reverseCol specifies an existing two-way reference column")
ref_table_id = get_referenced_table_id(col_type)
if not ref_table_id:
raise ValueError("reverseCol may only be set on a column with a reference type")
if desired_reverse_col.tableId != ref_table_id:
raise ValueError("reverseCol must be a column in the target table")
def pick_reverse_col_label(docmodel, col_rec):
ref_table_id = get_referenced_table_id(col_rec.type)
ref_table_rec = docmodel.get_table_rec(ref_table_id)
# First try the source table title.
source_table_rec = col_rec.parentId
reverse_label = source_table_rec.rawViewSectionRef.title or source_table_rec.tableId
# If that name already exists (as a label), add the source column's name as a suffix.
avoid_set = set(c.label for c in ref_table_rec.columns)
if reverse_label in avoid_set:
return reverse_label + "-" + (col_rec.label or col_rec.colId)
return reverse_label