Skip to content

Commit b5fe8cf

Browse files
authored
[ENG-5427] fix table docs errors (#1265)
* fix table docs errors * Include reflex.db in version control --------- Co-authored-by: pourhakimi <[email protected]>
1 parent 68123c1 commit b5fe8cf

File tree

2 files changed

+125
-20
lines changed

2 files changed

+125
-20
lines changed

docs/library/tables-and-data-grids/table.md

Lines changed: 125 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -230,18 +230,115 @@ class Person:
230230
email: str
231231
group: str
232232
```
233-
234233
## Sorting and Filtering (Searching)
235234

236-
In this example we sort and filter the data.
235+
In this example we show two approaches to sort and filter data:
236+
1. Using SQL-like operations for database-backed models (simulated)
237+
2. Using Python operations for in-memory data
238+
239+
Both approaches use the same UI components: `rx.select` for sorting and `rx.input` for filtering.
240+
241+
### Approach 1: Database Filtering and Sorting
242+
243+
For database-backed models, we typically use SQL queries with `select`, `where`, and `order_by`. In this example, we'll simulate this behavior with mock data.
244+
245+
246+
```python demo exec
247+
# Simulating database operations with mock data
248+
class DatabaseTableState(rx.State):
249+
# Mock data to simulate database records
250+
users: list = [
251+
{"name": "John Doe", "email": "[email protected]", "phone": "555-1234", "address": "123 Main St"},
252+
{"name": "Jane Smith", "email": "[email protected]", "phone": "555-5678", "address": "456 Oak Ave"},
253+
{"name": "Bob Johnson", "email": "[email protected]", "phone": "555-9012", "address": "789 Pine Rd"},
254+
{"name": "Alice Brown", "email": "[email protected]", "phone": "555-3456", "address": "321 Maple Dr"},
255+
]
256+
filtered_users: list[dict] = []
257+
sort_value = ""
258+
search_value = ""
259+
260+
261+
@rx.event
262+
def load_entries(self):
263+
"""Simulate querying the database with filter and sort."""
264+
# Start with all users
265+
result = self.users.copy()
266+
267+
# Apply filtering if search value exists
268+
if self.search_value != "":
269+
search_term = self.search_value.lower()
270+
result = [
271+
user for user in result
272+
if any(search_term in str(value).lower() for value in user.values())
273+
]
274+
275+
# Apply sorting if sort column is selected
276+
if self.sort_value != "":
277+
result = sorted(result, key=lambda x: x[self.sort_value])
278+
279+
self.filtered_users = result
280+
yield
281+
282+
@rx.event
283+
def sort_values(self, sort_value):
284+
"""Update sort value and reload data."""
285+
self.sort_value = sort_value
286+
yield self.load_entries()
287+
288+
@rx.event
289+
def filter_values(self, search_value):
290+
"""Update search value and reload data."""
291+
self.search_value = search_value
292+
yield self.load_entries()
293+
294+
295+
def show_customer(user):
296+
"""Show a customer in a table row."""
297+
return rx.table.row(
298+
rx.table.cell(user["name"]),
299+
rx.table.cell(user["email"]),
300+
rx.table.cell(user["phone"]),
301+
rx.table.cell(user["address"]),
302+
)
303+
304+
305+
def database_table_example():
306+
return rx.vstack(
307+
rx.select(
308+
["name", "email", "phone", "address"],
309+
placeholder="Sort By: Name",
310+
on_change=lambda value: DatabaseTableState.sort_values(value),
311+
),
312+
rx.input(
313+
placeholder="Search here...",
314+
on_change=lambda value: DatabaseTableState.filter_values(value),
315+
),
316+
rx.table.root(
317+
rx.table.header(
318+
rx.table.row(
319+
rx.table.column_header_cell("Name"),
320+
rx.table.column_header_cell("Email"),
321+
rx.table.column_header_cell("Phone"),
322+
rx.table.column_header_cell("Address"),
323+
),
324+
),
325+
rx.table.body(rx.foreach(DatabaseTableState.filtered_users, show_customer)),
326+
on_mount=DatabaseTableState.load_entries,
327+
width="100%",
328+
),
329+
width="100%",
330+
)
331+
```
237332

238-
The state variable `_people` is set to be a [backend-only variable]({vars.base_vars.path}). This is done in case the variable is very large in order to reduce network traffic and improve performance.
333+
### Approach 2: In-Memory Filtering and Sorting
239334

240-
For sorting the `rx.select` component is used. The data is sorted based on the attributes of the `Person` class. When a `select` item is selected, as the `on_change` event trigger is hooked up to the `set_sort_value` event handler, the data is sorted based on the state variable `sort_value` attribute selected. (Every base var has a [built-in event handler to set]({events.setters.path}) it's value for convenience, called `set_VARNAME`.)
335+
For in-memory data, we use Python operations like `sorted()` and list comprehensions.
241336

242-
For filtering the `rx.input` component is used. The data is filtered based on the search query entered into the `rx.input` component. When a search query is entered, as the `on_change` event trigger is hooked up to the `set_search_value` event handler, the data is filtered based on if the state variable `search_value` is present in any of the data in that specific `Person`.
337+
The state variable `_people` is set to be a backend-only variable. This is done in case the variable is very large in order to reduce network traffic and improve performance.
243338

244-
`current_people` is an [`rx.var(cache=True)`]({vars.computed_vars.path}). It is a var that is only recomputed when the other state vars it depends on change. This is to ensure that the `People` shown in the table are always up to date whenever they are searched or sorted.
339+
When a `select` item is selected, the `on_change` event trigger is hooked up to the `set_sort_value` event handler. Every base var has a built-in event handler to set its value for convenience, called `set_VARNAME`.
340+
341+
`current_people` is an `rx.var(cache=True)`. It is a var that is only recomputed when the other state vars it depends on change. This ensures that the `People` shown in the table are always up to date whenever they are searched or sorted.
245342

246343
```python demo exec
247344
import dataclasses
@@ -253,7 +350,7 @@ class Person:
253350
group: str
254351

255352

256-
class TableSortingState(rx.State):
353+
class InMemoryTableState(rx.State):
257354

258355
_people: list[Person] = [
259356
Person(full_name="Danilo Sousa", email="[email protected]", group="Developer"),
@@ -277,31 +374,31 @@ class TableSortingState(rx.State):
277374
people = [
278375
person for person in people
279376
if any(
280-
self.search_value in getattr(person, attr).lower()
377+
self.search_value.lower() in getattr(person, attr).lower()
281378
for attr in ['full_name', 'email', 'group']
282379
)
283380
]
284381
return people
285382

286383

287-
def show_person(person: list):
384+
def show_person(person: Person):
288385
"""Show a person in a table row."""
289386
return rx.table.row(
290387
rx.table.cell(person.full_name),
291388
rx.table.cell(person.email),
292389
rx.table.cell(person.group),
293390
)
294391

295-
def sorting_table_example():
392+
def in_memory_table_example():
296393
return rx.vstack(
297394
rx.select(
298395
["full_name", "email", "group"],
299396
placeholder="Sort By: full_name",
300-
on_change=TableSortingState.set_sort_value,
397+
on_change=InMemoryTableState.set_sort_value,
301398
),
302399
rx.input(
303400
placeholder="Search here...",
304-
on_change=TableSortingState.set_search_value,
401+
on_change=InMemoryTableState.set_search_value,
305402
),
306403
rx.table.root(
307404
rx.table.header(
@@ -311,13 +408,20 @@ def sorting_table_example():
311408
rx.table.column_header_cell("Group"),
312409
),
313410
),
314-
rx.table.body(rx.foreach(TableSortingState.current_people, show_person)),
411+
rx.table.body(rx.foreach(InMemoryTableState.current_people, show_person)),
315412
width="100%",
316413
),
317414
width="100%",
318415
)
319416
```
320417

418+
### When to Use Each Approach
419+
420+
- **Database Approach**: Best for large datasets or when the data already exists in a database
421+
- **In-Memory Approach**: Best for smaller datasets, prototyping, or when the data is static or loaded from an API
422+
423+
Both approaches provide the same user experience with filtering and sorting functionality.
424+
321425
# Database
322426

323427
The more common use case for building an `rx.table` is to use data from a database.
@@ -335,7 +439,6 @@ If you want to load the data when the page in the app loads you can set `on_load
335439
```python
336440
class Customer(rx.Model, table=True):
337441
"""The customer model."""
338-
339442
name: str
340443
email: str
341444
phone: str
@@ -350,7 +453,7 @@ class DatabaseTableState(rx.State):
350453
users: list[Customer] = []
351454

352455
@rx.event
353-
def load_entries(self) -> list[Customer]:
456+
def load_entries(self):
354457
"""Get all users from the database."""
355458
with rx.session() as session:
356459
self.users = session.exec(select(Customer)).all()
@@ -416,7 +519,7 @@ class DatabaseTableState2(rx.State):
416519
search_value = ""
417520

418521
@rx.event
419-
def load_entries(self) -> list[Customer]:
522+
def load_entries(self):
420523
"""Get all users from the database."""
421524
with rx.session() as session:
422525
query = select(Customer)
@@ -441,11 +544,13 @@ class DatabaseTableState2(rx.State):
441544

442545
@rx.event
443546
def sort_values(self, sort_value):
547+
print(sort_value)
444548
self.sort_value = sort_value
445549
self.load_entries()
446550

447551
@rx.event
448552
def filter_values(self, search_value):
553+
print(search_value)
449554
self.search_value = search_value
450555
self.load_entries()
451556

@@ -489,6 +594,7 @@ def loading_data_table_example2():
489594

490595
```
491596

597+
492598
## Pagination
493599

494600
Pagination is an important part of database management, especially when working with large datasets. It helps in enabling efficient data retrieval by breaking down results into manageable loads.
@@ -541,7 +647,7 @@ class DatabaseTableState3(rx.State):
541647
self.total_items = session.exec(select(func.count(Customer.id))).one()
542648

543649
@rx.event
544-
def load_entries(self) -> list[Customer]:
650+
def load_entries(self):
545651
"""Get all users from the database."""
546652
with rx.session() as session:
547653
query = select(Customer)
@@ -595,7 +701,6 @@ def loading_data_table_example3():
595701
)
596702

597703
```
598-
599704
## More advanced examples
600705

601706
The real power of the `rx.table` comes where you are able to visualise, add and edit data live in your app. Check out these apps and code to see how this is done: app: https://customer-data-app.reflex.run code: https://github.com/reflex-dev/reflex-examples/tree/main/customer_data_app and code: https://github.com/reflex-dev/data-viewer.
@@ -620,7 +725,7 @@ class TableDownloadState(rx.State):
620725
users: list[Customer] = []
621726

622727
@rx.event
623-
def load_entries(self) -> list[Customer]:
728+
def load_entries(self):
624729
"""Get all users from the database."""
625730
with rx.session() as session:
626731
self.users = session.exec(select(Customer)).all()
@@ -743,4 +848,4 @@ rx.flex(
743848
direction="column",
744849
spacing="2",
745850
)
746-
```
851+
```

reflex.db

16 KB
Binary file not shown.

0 commit comments

Comments
 (0)