Skip to content

Commit 7c921b0

Browse files
Add support for computed_field in Table component. (#321)
1 parent 97c4f07 commit 7c921b0

File tree

2 files changed

+49
-16
lines changed

2 files changed

+49
-16
lines changed

src/python-fastui/fastui/components/tables.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,21 @@ def _fill_columns(self) -> _te.Self:
4444
raise ValueError('Cannot infer model from empty data, please set `Table(..., model=MyModel)`')
4545

4646
if self.columns is None:
47-
self.columns = [
48-
display.DisplayLookup(field=name, title=field.title)
49-
for name, field in data_model_type.model_fields.items()
50-
]
47+
self.columns = []
48+
for name, field in data_model_type.model_fields.items():
49+
self.columns.append(display.DisplayLookup(field=name, title=field.title))
50+
for name, field in data_model_type.model_computed_fields.items():
51+
self.columns.append(display.DisplayLookup(field=name, title=field.title))
52+
5153
else:
5254
# add pydantic titles to columns that don't have them
5355
for column in (c for c in self.columns if c.title is None):
54-
field = data_model_type.model_fields.get(column.field)
55-
if field and field.title:
56-
column.title = field.title
56+
model_field = data_model_type.model_fields.get(column.field)
57+
computed_field = data_model_type.model_computed_fields.get(column.field)
58+
if model_field and model_field.title:
59+
column.title = model_field.title
60+
elif computed_field and computed_field.title:
61+
column.title = computed_field.title
5762
return self
5863

5964
@classmethod

src/python-fastui/tests/test_tables_display.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import pytest
22
from fastui import components
33
from fastui.components import display
4-
from pydantic import BaseModel, Field
4+
from pydantic import BaseModel, Field, computed_field
55

66

77
class User(BaseModel):
88
id: int
99
name: str = Field(title='Name')
1010

11+
@computed_field(title='Representation')
12+
@property
13+
def representation(self) -> str:
14+
return f'{self.id}: {self.name}'
15+
1116

1217
users = [User(id=1, name='john'), User(id=2, name='jack')]
1318

@@ -17,21 +22,40 @@ def test_table_no_columns():
1722

1823
# insert_assert(table.model_dump(by_alias=True, exclude_none=True))
1924
assert table.model_dump(by_alias=True, exclude_none=True) == {
20-
'data': [{'id': 1, 'name': 'john'}, {'id': 2, 'name': 'jack'}],
21-
'columns': [{'field': 'id'}, {'field': 'name', 'title': 'Name'}],
25+
'data': [
26+
{'id': 1, 'name': 'john', 'representation': '1: john'},
27+
{'id': 2, 'name': 'jack', 'representation': '2: jack'},
28+
],
29+
'columns': [
30+
{'field': 'id'},
31+
{'field': 'name', 'title': 'Name'},
32+
{'field': 'representation', 'title': 'Representation'},
33+
],
2234
'type': 'Table',
2335
}
2436

2537

2638
def test_table_columns():
2739
table = components.Table(
28-
data=users, columns=[display.DisplayLookup(field='id', title='ID'), display.DisplayLookup(field='name')]
40+
data=users,
41+
columns=[
42+
display.DisplayLookup(field='id', title='ID'),
43+
display.DisplayLookup(field='name'),
44+
display.DisplayLookup(field='representation'),
45+
],
2946
)
3047

3148
# insert_assert(table.model_dump(by_alias=True, exclude_none=True))
3249
assert table.model_dump(by_alias=True, exclude_none=True) == {
33-
'data': [{'id': 1, 'name': 'john'}, {'id': 2, 'name': 'jack'}],
34-
'columns': [{'title': 'ID', 'field': 'id'}, {'field': 'name', 'title': 'Name'}],
50+
'data': [
51+
{'id': 1, 'name': 'john', 'representation': '1: john'},
52+
{'id': 2, 'name': 'jack', 'representation': '2: jack'},
53+
],
54+
'columns': [
55+
{'title': 'ID', 'field': 'id'},
56+
{'title': 'Name', 'field': 'name'},
57+
{'title': 'Representation', 'field': 'representation'},
58+
],
3559
'type': 'Table',
3660
}
3761

@@ -47,7 +71,11 @@ def test_table_empty_data_model():
4771
# insert_assert(table.model_dump(by_alias=True, exclude_none=True))
4872
assert table.model_dump(by_alias=True, exclude_none=True) == {
4973
'data': [],
50-
'columns': [{'field': 'id'}, {'title': 'Name', 'field': 'name'}],
74+
'columns': [
75+
{'field': 'id'},
76+
{'title': 'Name', 'field': 'name'},
77+
{'title': 'Representation', 'field': 'representation'},
78+
],
5179
'type': 'Table',
5280
}
5381

@@ -57,7 +85,7 @@ def test_display_no_fields():
5785

5886
# insert_assert(d.model_dump(by_alias=True, exclude_none=True))
5987
assert d.model_dump(by_alias=True, exclude_none=True) == {
60-
'data': {'id': 1, 'name': 'john'},
88+
'data': {'id': 1, 'name': 'john', 'representation': '1: john'},
6189
'fields': [{'field': 'id'}, {'title': 'Name', 'field': 'name'}],
6290
'type': 'Details',
6391
}
@@ -70,7 +98,7 @@ def test_display_fields():
7098

7199
# insert_assert(d.model_dump(by_alias=True, exclude_none=True))
72100
assert d.model_dump(by_alias=True, exclude_none=True) == {
73-
'data': {'id': 1, 'name': 'john'},
101+
'data': {'id': 1, 'name': 'john', 'representation': '1: john'},
74102
'fields': [{'title': 'ID', 'field': 'id'}, {'title': 'Name', 'field': 'name'}],
75103
'type': 'Details',
76104
}

0 commit comments

Comments
 (0)