diff --git a/src/python-fastui/fastui/components/tables.py b/src/python-fastui/fastui/components/tables.py index 9044ef3d..22328d51 100644 --- a/src/python-fastui/fastui/components/tables.py +++ b/src/python-fastui/fastui/components/tables.py @@ -44,16 +44,21 @@ def _fill_columns(self) -> _te.Self: raise ValueError('Cannot infer model from empty data, please set `Table(..., model=MyModel)`') if self.columns is None: - self.columns = [ - display.DisplayLookup(field=name, title=field.title) - for name, field in data_model_type.model_fields.items() - ] + self.columns = [] + for name, field in data_model_type.model_fields.items(): + self.columns.append(display.DisplayLookup(field=name, title=field.title)) + for name, field in data_model_type.model_computed_fields.items(): + self.columns.append(display.DisplayLookup(field=name, title=field.title)) + else: # add pydantic titles to columns that don't have them for column in (c for c in self.columns if c.title is None): - field = data_model_type.model_fields.get(column.field) - if field and field.title: - column.title = field.title + model_field = data_model_type.model_fields.get(column.field) + computed_field = data_model_type.model_computed_fields.get(column.field) + if model_field and model_field.title: + column.title = model_field.title + elif computed_field and computed_field.title: + column.title = computed_field.title return self @classmethod diff --git a/src/python-fastui/tests/test_tables_display.py b/src/python-fastui/tests/test_tables_display.py index d1ab281b..c1c2274b 100644 --- a/src/python-fastui/tests/test_tables_display.py +++ b/src/python-fastui/tests/test_tables_display.py @@ -1,13 +1,18 @@ import pytest from fastui import components from fastui.components import display -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, computed_field class User(BaseModel): id: int name: str = Field(title='Name') + @computed_field(title='Representation') + @property + def representation(self) -> str: + return f'{self.id}: {self.name}' + users = [User(id=1, name='john'), User(id=2, name='jack')] @@ -17,21 +22,40 @@ def test_table_no_columns(): # insert_assert(table.model_dump(by_alias=True, exclude_none=True)) assert table.model_dump(by_alias=True, exclude_none=True) == { - 'data': [{'id': 1, 'name': 'john'}, {'id': 2, 'name': 'jack'}], - 'columns': [{'field': 'id'}, {'field': 'name', 'title': 'Name'}], + 'data': [ + {'id': 1, 'name': 'john', 'representation': '1: john'}, + {'id': 2, 'name': 'jack', 'representation': '2: jack'}, + ], + 'columns': [ + {'field': 'id'}, + {'field': 'name', 'title': 'Name'}, + {'field': 'representation', 'title': 'Representation'}, + ], 'type': 'Table', } def test_table_columns(): table = components.Table( - data=users, columns=[display.DisplayLookup(field='id', title='ID'), display.DisplayLookup(field='name')] + data=users, + columns=[ + display.DisplayLookup(field='id', title='ID'), + display.DisplayLookup(field='name'), + display.DisplayLookup(field='representation'), + ], ) # insert_assert(table.model_dump(by_alias=True, exclude_none=True)) assert table.model_dump(by_alias=True, exclude_none=True) == { - 'data': [{'id': 1, 'name': 'john'}, {'id': 2, 'name': 'jack'}], - 'columns': [{'title': 'ID', 'field': 'id'}, {'field': 'name', 'title': 'Name'}], + 'data': [ + {'id': 1, 'name': 'john', 'representation': '1: john'}, + {'id': 2, 'name': 'jack', 'representation': '2: jack'}, + ], + 'columns': [ + {'title': 'ID', 'field': 'id'}, + {'title': 'Name', 'field': 'name'}, + {'title': 'Representation', 'field': 'representation'}, + ], 'type': 'Table', } @@ -47,7 +71,11 @@ def test_table_empty_data_model(): # insert_assert(table.model_dump(by_alias=True, exclude_none=True)) assert table.model_dump(by_alias=True, exclude_none=True) == { 'data': [], - 'columns': [{'field': 'id'}, {'title': 'Name', 'field': 'name'}], + 'columns': [ + {'field': 'id'}, + {'title': 'Name', 'field': 'name'}, + {'title': 'Representation', 'field': 'representation'}, + ], 'type': 'Table', } @@ -57,7 +85,7 @@ def test_display_no_fields(): # insert_assert(d.model_dump(by_alias=True, exclude_none=True)) assert d.model_dump(by_alias=True, exclude_none=True) == { - 'data': {'id': 1, 'name': 'john'}, + 'data': {'id': 1, 'name': 'john', 'representation': '1: john'}, 'fields': [{'field': 'id'}, {'title': 'Name', 'field': 'name'}], 'type': 'Details', } @@ -70,7 +98,7 @@ def test_display_fields(): # insert_assert(d.model_dump(by_alias=True, exclude_none=True)) assert d.model_dump(by_alias=True, exclude_none=True) == { - 'data': {'id': 1, 'name': 'john'}, + 'data': {'id': 1, 'name': 'john', 'representation': '1: john'}, 'fields': [{'title': 'ID', 'field': 'id'}, {'title': 'Name', 'field': 'name'}], 'type': 'Details', }