mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Barely working reference lists in frontend
Summary: This makes it possible to set the type of a column to ReferenceList, but the UI is terrible ReferenceList.ts is a mishmash of ChoiceList and Reference that sort of works but something about the CSS is clearly broken ReferenceListEditor is just a text editor, you have to type in a JSON array of row IDs. Ignore the value that's present when you start editing. I can maybe try mashing together ReferenceEditor and ChoiceListEditor but it doesn't seem wise. I think @georgegevoian should take over here. Reviewing the diff as it is to check for obvious issues is probably good but I don't think it's worth trying to land/merge anything. Test Plan: none Reviewers: dsagal Reviewed By: dsagal Subscribers: georgegevoian Differential Revision: https://phab.getgrist.com/D2914
This commit is contained in:
@@ -440,6 +440,14 @@ class ReferenceListColumn(BaseReferenceColumn):
|
||||
ReferenceListColumn maintains for each row a list of references (row IDs) into another table.
|
||||
Accessing them yields RecordSets.
|
||||
"""
|
||||
def set(self, row_id, value):
|
||||
if isinstance(value, six.string_types) and value.startswith(u'['):
|
||||
try:
|
||||
value = json.loads(value)
|
||||
except Exception:
|
||||
pass
|
||||
super(ReferenceListColumn, self).set(row_id, value)
|
||||
|
||||
def _update_references(self, row_id, old_list, new_list):
|
||||
for old_value in old_list or ():
|
||||
self._relation.remove_reference(row_id, old_value)
|
||||
|
||||
@@ -188,10 +188,10 @@ def ISREF(value):
|
||||
"""
|
||||
Checks whether a value is a table record.
|
||||
|
||||
For example, if a column person is of type Reference to the People table, then ISREF($person)
|
||||
is True.
|
||||
Similarly, ISREF(People.lookupOne(name=$name)) is True. For any other type of value,
|
||||
ISREF() would evaluate to False.
|
||||
For example, if a column `person` is of type Reference to the `People` table,
|
||||
then `ISREF($person)` is `True`.
|
||||
Similarly, `ISREF(People.lookupOne(name=$name))` is `True`. For any other type of value,
|
||||
`ISREF()` would evaluate to `False`.
|
||||
|
||||
>>> ISREF(17)
|
||||
False
|
||||
@@ -202,6 +202,25 @@ def ISREF(value):
|
||||
return isinstance(value, Record)
|
||||
|
||||
|
||||
def ISREFLIST(value):
|
||||
"""
|
||||
Checks whether a value is a [`RecordSet`](#recordset),
|
||||
the type of values in Reference List columns.
|
||||
|
||||
For example, if a column `people` is of type Reference List to the `People` table,
|
||||
then `ISREFLIST($people)` is `True`.
|
||||
Similarly, `ISREFLIST(People.lookupRecords(name=$name))` is `True`. For any other type of value,
|
||||
`ISREFLIST()` would evaluate to `False`.
|
||||
|
||||
>>> ISREFLIST(17)
|
||||
False
|
||||
>>> ISREFLIST("Roger")
|
||||
False
|
||||
|
||||
"""
|
||||
return isinstance(value, RecordSet)
|
||||
|
||||
|
||||
def ISTEXT(value):
|
||||
"""
|
||||
Checks whether a value is text.
|
||||
|
||||
@@ -286,7 +286,7 @@ class RecordList(list):
|
||||
self._sort_by = sort_by
|
||||
|
||||
def __repr__(self):
|
||||
return "RecordList(%r, group_by=%r, sort_by=%r)" % (
|
||||
return "RecordList(%s, group_by=%r, sort_by=%r)" % (
|
||||
list.__repr__(self), self._group_by, self._sort_by)
|
||||
|
||||
|
||||
|
||||
@@ -121,11 +121,16 @@ class SummaryActions(object):
|
||||
"""
|
||||
key = tuple(sorted(int(c) for c in source_groupby_columns))
|
||||
|
||||
groupby_colinfo = [_make_col_info(col=c,
|
||||
isFormula=False,
|
||||
formula='',
|
||||
type='Choice' if c.type == 'ChoiceList' else c.type)
|
||||
for c in source_groupby_columns]
|
||||
groupby_colinfo = [
|
||||
_make_col_info(
|
||||
col=c,
|
||||
isFormula=False,
|
||||
formula='',
|
||||
type='Choice' if c.type == 'ChoiceList' else
|
||||
c.type.replace('RefList:', 'Ref:')
|
||||
)
|
||||
for c in source_groupby_columns
|
||||
]
|
||||
summary_table = next((t for t in source_table.summaryTables if t.summaryKey == key), None)
|
||||
created = False
|
||||
if not summary_table:
|
||||
|
||||
@@ -270,7 +270,7 @@ class Table(object):
|
||||
self._summary_simple = not any(
|
||||
isinstance(
|
||||
self._summary_source_table.all_columns.get(group_col),
|
||||
column.ChoiceListColumn
|
||||
(column.ChoiceListColumn, column.ReferenceListColumn)
|
||||
)
|
||||
for group_col in groupby_cols
|
||||
)
|
||||
@@ -299,12 +299,13 @@ class Table(object):
|
||||
@usertypes.formulaType(usertypes.ReferenceList(summary_table.table_id))
|
||||
def _updateSummary(rec, table): # pylint: disable=unused-argument
|
||||
# Create a row in the summary table for every combination of values in
|
||||
# ChoiceList columns
|
||||
# list type columns
|
||||
lookup_values = []
|
||||
for group_col in groupby_cols:
|
||||
lookup_value = getattr(rec, group_col)
|
||||
if isinstance(self.all_columns[group_col], column.ChoiceListColumn):
|
||||
# Check that ChoiceList cells have appropriate types.
|
||||
if isinstance(self.all_columns[group_col],
|
||||
(column.ChoiceListColumn, column.ReferenceListColumn)):
|
||||
# Check that ChoiceList/ReferenceList cells have appropriate types.
|
||||
# Don't iterate over characters of a string.
|
||||
if isinstance(lookup_value, (six.binary_type, six.text_type)):
|
||||
return []
|
||||
|
||||
@@ -453,6 +453,14 @@ class ReferenceList(BaseColumnType):
|
||||
return "RefList"
|
||||
|
||||
def do_convert(self, value):
|
||||
if isinstance(value, six.string_types):
|
||||
# If it's a string that looks like JSON, try to parse it as such.
|
||||
if value.startswith('['):
|
||||
try:
|
||||
value = json.loads(value)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if isinstance(value, RecordSet):
|
||||
assert value._table.table_id == self.table_id
|
||||
return objtypes.RecordList(value._row_ids, group_by=value._group_by, sort_by=value._sort_by)
|
||||
@@ -466,9 +474,24 @@ class ReferenceList(BaseColumnType):
|
||||
return value is None or (isinstance(value, list) and
|
||||
all(Reference.is_right_type(val) for val in value))
|
||||
|
||||
@classmethod
|
||||
def typeConvert(cls, value, ref_table, visible_col=None): # noqa # pylint: disable=arguments-differ
|
||||
# TODO this is based on Reference.typeConvert.
|
||||
# It doesn't make much sense as a conversion but I don't know what would
|
||||
if ref_table and visible_col:
|
||||
return ref_table.lookupRecords(**{visible_col: value}) or six.text_type(value)
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
class Attachments(ReferenceList):
|
||||
"""
|
||||
Currently attachment type is the field for holding data for attachments.
|
||||
"""
|
||||
def __init__(self):
|
||||
super(Attachments, self).__init__('_grist_Attachments')
|
||||
|
||||
@classmethod
|
||||
def typeConvert(cls, value): # noqa # pylint: disable=arguments-differ
|
||||
# Don't use ReferenceList.typeConvert which is called with a different number of arguments
|
||||
return value
|
||||
|
||||
Reference in New Issue
Block a user