sqlmodel-object-helpers 0.0.5__tar.gz → 0.0.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/PKG-INFO +126 -2
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/README.md +125 -1
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/__init__.py +15 -3
- sqlmodel_object_helpers-0.0.6/src/sqlmodel_object_helpers/dynamic_meta.py +468 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/standalone.py +35 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/conftest.py +5 -1
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_datetime_range.py +51 -46
- sqlmodel_object_helpers-0.0.6/tests/test_dynamic_meta.py +1037 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_query.py +37 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_standalone.py +22 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/.github/workflows/publish.yml +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/.gitignore +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/LICENSE +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/pyproject.toml +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/constants.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/exceptions.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/filters.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/loaders.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/mutations.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/operators.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/query.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/session.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/types/__init__.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/types/columns.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/types/datetime.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/types/filters.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/types/pagination.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/src/sqlmodel_object_helpers/types/projections.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_bulk_mutations.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_column_meta.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_computed_columns.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_count_exists.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_exceptions.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_filters.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_for_update.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_generated_columns_pg.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_loaders.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_mutations.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_operators.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_settings.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_time_filter.py +0 -0
- {sqlmodel_object_helpers-0.0.5 → sqlmodel_object_helpers-0.0.6}/tests/test_types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlmodel-object-helpers
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: Generic async query helpers for SQLModel: filtering, eager loading, pagination
|
|
5
5
|
Project-URL: Homepage, https://github.com/itstandart/sqlmodel-object-helpers
|
|
6
6
|
Project-URL: Repository, https://github.com/itstandart/sqlmodel-object-helpers
|
|
@@ -55,6 +55,7 @@ Generic async query helpers for [SQLModel](https://sqlmodel.tiangolo.com/): filt
|
|
|
55
55
|
- **Relationship Safety Check** - `check_for_related_records` pre-deletion inspection of ONETOMANY dependencies
|
|
56
56
|
- **Security Limits** - Configurable depth, list size, and pagination caps to prevent abuse
|
|
57
57
|
- **Type Safety** - Full type annotations with PEP 695 generics and `py.typed` marker (PEP 561)
|
|
58
|
+
- **Dynamic Table Metadata** - `build_dynamic_meta` derives `TableMeta` + `list[ColumnMeta]` from a SQLModel class by reading PostgreSQL `pg_description` with fallback to `Column(comment=...)`. Supports per-role label/row_link overrides via `||` comment format, TTL-cached
|
|
58
59
|
- **Standalone Mode** - `configure()` + `import sqlmodel_object_helpers.standalone` for auto-session usage without DI
|
|
59
60
|
- **Session Lifecycle Logging** - Transparent session open/commit/rollback logging with hex session IDs and timing
|
|
60
61
|
|
|
@@ -619,6 +620,117 @@ order = soh.OrderBy(sorts=[
|
|
|
619
620
|
result = await soh.get_objects(session, User, order_by=order)
|
|
620
621
|
```
|
|
621
622
|
|
|
623
|
+
## Dynamic Table Metadata
|
|
624
|
+
|
|
625
|
+
`build_dynamic_meta` builds `TableMeta` + `list[ColumnMeta]` from a SQLModel class at runtime, reading PostgreSQL `pg_description` (TTL-cached) with fallback to `Column(comment=...)` model defaults.
|
|
626
|
+
|
|
627
|
+
```python
|
|
628
|
+
import sqlmodel_object_helpers as soh
|
|
629
|
+
|
|
630
|
+
# Physical columns — label, type, lookup derived automatically
|
|
631
|
+
table_meta, columns = await soh.build_dynamic_meta(
|
|
632
|
+
session,
|
|
633
|
+
EmailMessage,
|
|
634
|
+
name="email_messages",
|
|
635
|
+
columns=[
|
|
636
|
+
"id", # physical column — everything derived
|
|
637
|
+
"email_type_id", # FK on *_lkp → lookup_dict auto-derived
|
|
638
|
+
"updated_at",
|
|
639
|
+
soh.ColumnMeta( # virtual column — fully specified
|
|
640
|
+
json_path="last_editor.user_name",
|
|
641
|
+
label="Редактор",
|
|
642
|
+
type=soh.ColumnType.STRING,
|
|
643
|
+
),
|
|
644
|
+
],
|
|
645
|
+
role_type="operator", # per-role label/row_link overrides
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
# Returns (TableMeta, list[ColumnMeta]) ready for GetAllPagination
|
|
649
|
+
return soh.GetAllPagination(
|
|
650
|
+
table=table_meta,
|
|
651
|
+
columns=columns,
|
|
652
|
+
data=items,
|
|
653
|
+
pagination=pagination_r,
|
|
654
|
+
)
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
### Type derivation (SA type → ColumnType)
|
|
658
|
+
|
|
659
|
+
Physical column types are mapped automatically. Unknown types fall back to `string`.
|
|
660
|
+
|
|
661
|
+
| SQLAlchemy type | ColumnType |
|
|
662
|
+
|---|---|
|
|
663
|
+
| `Boolean` | `boolean` |
|
|
664
|
+
| `DateTime` | `datetime` |
|
|
665
|
+
| `Date` | `date` |
|
|
666
|
+
| `Integer`, `BigInteger`, `SmallInteger` | `integer` |
|
|
667
|
+
| `Numeric`, `Float` | `float` |
|
|
668
|
+
| `String`, `Text`, `Enum`, `Interval`, `ARRAY`, `Uuid` | `string` |
|
|
669
|
+
| Any other type | `string` (fallback) |
|
|
670
|
+
|
|
671
|
+
### Label precedence (per physical column)
|
|
672
|
+
|
|
673
|
+
1. `pg_description` — DBA edit via `COMMENT ON COLUMN` (supports per-role `||` overrides)
|
|
674
|
+
2. `Column(comment=...)` — Python-side default in the model
|
|
675
|
+
3. `ValueError` — fail-loud if both are missing
|
|
676
|
+
|
|
677
|
+
### Per-role overrides via `||` format
|
|
678
|
+
|
|
679
|
+
DBAs can set role-specific labels and row links in PostgreSQL comments:
|
|
680
|
+
|
|
681
|
+
```sql
|
|
682
|
+
COMMENT ON COLUMN emails.email_type_id IS 'Тип письма||operator=Категория||buh=Реквизит';
|
|
683
|
+
COMMENT ON TABLE emails IS 'Письма||row_link=/email/$id||row_link.operator=/op/email/$id';
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### TTL cache
|
|
687
|
+
|
|
688
|
+
`pg_description` queries are cached per `(schema, table)` with a configurable TTL (default 60s):
|
|
689
|
+
|
|
690
|
+
```python
|
|
691
|
+
soh.configure_meta_cache_ttl(120) # change TTL to 120 seconds
|
|
692
|
+
soh.invalidate_meta_cache() # clear entire cache
|
|
693
|
+
soh.invalidate_meta_cache(schema="lead") # clear all entries for a schema
|
|
694
|
+
soh.invalidate_meta_cache("lead", "emails") # clear one entry
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### Standalone mode
|
|
698
|
+
|
|
699
|
+
```python
|
|
700
|
+
import sqlmodel_object_helpers.standalone as soh_sa
|
|
701
|
+
|
|
702
|
+
table_meta, columns = await soh_sa.build_dynamic_meta(
|
|
703
|
+
EmailMessage,
|
|
704
|
+
name="email_messages",
|
|
705
|
+
columns=["id", "email_type_id"],
|
|
706
|
+
)
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
### Backward compatibility and migration
|
|
710
|
+
|
|
711
|
+
`GetAllPagination` is fully backward compatible — `table` and `columns` default to `None`, so existing endpoints continue to work without changes:
|
|
712
|
+
|
|
713
|
+
```python
|
|
714
|
+
# Before (still works as-is)
|
|
715
|
+
return soh.GetAllPagination(data=items, pagination=pagination_r)
|
|
716
|
+
|
|
717
|
+
# After (meta added when ready)
|
|
718
|
+
return soh.GetAllPagination(
|
|
719
|
+
table=table_meta,
|
|
720
|
+
columns=columns,
|
|
721
|
+
data=items,
|
|
722
|
+
pagination=pagination_r,
|
|
723
|
+
)
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
Endpoints can be migrated one at a time. Three strategies per endpoint:
|
|
727
|
+
|
|
728
|
+
| Strategy | `table`/`columns` | Use case |
|
|
729
|
+
|---|---|---|
|
|
730
|
+
| **No meta** | Always `None` | Endpoint not yet migrated, frontend uses hardcoded table |
|
|
731
|
+
| **Always meta** | Sent on every request | Simple, no frontend caching logic needed |
|
|
732
|
+
| **Meta on first page** | Sent when `page=1`, `None` on pages 2+ | Saves traffic, frontend caches meta from first response |
|
|
733
|
+
|
|
622
734
|
## API Reference
|
|
623
735
|
|
|
624
736
|
### Session Management
|
|
@@ -629,7 +741,7 @@ result = await soh.get_objects(session, User, order_by=order)
|
|
|
629
741
|
|
|
630
742
|
### Standalone Wrappers
|
|
631
743
|
|
|
632
|
-
- `import sqlmodel_object_helpers.standalone as soh_sa` -- All 12 query/mutation functions without `session` parameter
|
|
744
|
+
- `import sqlmodel_object_helpers.standalone as soh_sa` -- All 12 query/mutation functions plus `build_dynamic_meta` without `session` parameter
|
|
633
745
|
|
|
634
746
|
### Settings
|
|
635
747
|
|
|
@@ -654,6 +766,14 @@ result = await soh.get_objects(session, User, order_by=order)
|
|
|
654
766
|
- `soh.delete_objects(session, model, filters)` -- Bulk delete via single `DELETE ... WHERE`
|
|
655
767
|
- `soh.check_for_related_records(session, model, pk)` -- Pre-deletion dependency check
|
|
656
768
|
|
|
769
|
+
### Dynamic Meta
|
|
770
|
+
|
|
771
|
+
- `soh.build_dynamic_meta(session, model, *, name, columns, header, row_link, role_type)` -- Build `TableMeta` + `list[ColumnMeta]` from model + `pg_description`
|
|
772
|
+
- `soh.load_pg_comments(session, schema, table)` -- Read `(table_comment, {col: comment})` from `pg_description` (TTL-cached)
|
|
773
|
+
- `soh.configure_meta_cache_ttl(seconds)` -- Override default `pg_description` cache TTL (default: 60s)
|
|
774
|
+
- `soh.invalidate_meta_cache(schema, table)` -- Manually invalidate the `pg_description` cache (full, by-schema, or by-table)
|
|
775
|
+
- `soh.ColumnEntry` -- Type alias: `str | ColumnMeta` (element of `columns` list in `build_dynamic_meta`)
|
|
776
|
+
|
|
657
777
|
### Filter Builders
|
|
658
778
|
|
|
659
779
|
- `soh.build_filter(model, filters, ...)` -- Build SQLAlchemy expression from LogicalFilter dict
|
|
@@ -762,6 +882,10 @@ This project follows [Semantic Versioning](https://semver.org/) (MAJOR.MINOR.PAT
|
|
|
762
882
|
|
|
763
883
|
## Changelog
|
|
764
884
|
|
|
885
|
+
### 0.0.6
|
|
886
|
+
|
|
887
|
+
- **build_dynamic_meta** / **load_pg_comments** / **configure_meta_cache_ttl** / **invalidate_meta_cache** — dynamic UI metadata reader. Builds `TableMeta` and `list[ColumnMeta]` from a SQLModel class by reading PostgreSQL `pg_description` (TTL-cached, default 60s) with fallback to `Column(comment=...)` defaults from the model. Supports per-role label/row_link overrides via extended `||` comment format. For physical columns the label precedence is `pg_description` > `Column(comment=...)` model default. For virtual columns (paths with `.` traversing relationships) and full overrides — pass a fully-specified `ColumnMeta` instance directly in the `columns` list (used as-is, no derivation). Lookup `lookup_dict`/`lookup_path` are derived from FK on `*_lkp` tables by `{schema}_{base}` convention. Type derived from SA column type. DBAs can edit labels via `COMMENT ON COLUMN/TABLE` SQL — frontend reflects changes within cache TTL without redeploy. No sync block, no schema migrations introduced; the application's `db.py` is not touched.
|
|
888
|
+
|
|
765
889
|
### 0.0.5
|
|
766
890
|
|
|
767
891
|
- **FilterDatetimeRange** / **FilterNaiveDatetimeRange** — range filters that parse comma-separated date strings (`"2026-04-01,2026-05-06"`) into `gt`/`lt` operators for SQL filtering
|
|
@@ -21,6 +21,7 @@ Generic async query helpers for [SQLModel](https://sqlmodel.tiangolo.com/): filt
|
|
|
21
21
|
- **Relationship Safety Check** - `check_for_related_records` pre-deletion inspection of ONETOMANY dependencies
|
|
22
22
|
- **Security Limits** - Configurable depth, list size, and pagination caps to prevent abuse
|
|
23
23
|
- **Type Safety** - Full type annotations with PEP 695 generics and `py.typed` marker (PEP 561)
|
|
24
|
+
- **Dynamic Table Metadata** - `build_dynamic_meta` derives `TableMeta` + `list[ColumnMeta]` from a SQLModel class by reading PostgreSQL `pg_description` with fallback to `Column(comment=...)`. Supports per-role label/row_link overrides via `||` comment format, TTL-cached
|
|
24
25
|
- **Standalone Mode** - `configure()` + `import sqlmodel_object_helpers.standalone` for auto-session usage without DI
|
|
25
26
|
- **Session Lifecycle Logging** - Transparent session open/commit/rollback logging with hex session IDs and timing
|
|
26
27
|
|
|
@@ -585,6 +586,117 @@ order = soh.OrderBy(sorts=[
|
|
|
585
586
|
result = await soh.get_objects(session, User, order_by=order)
|
|
586
587
|
```
|
|
587
588
|
|
|
589
|
+
## Dynamic Table Metadata
|
|
590
|
+
|
|
591
|
+
`build_dynamic_meta` builds `TableMeta` + `list[ColumnMeta]` from a SQLModel class at runtime, reading PostgreSQL `pg_description` (TTL-cached) with fallback to `Column(comment=...)` model defaults.
|
|
592
|
+
|
|
593
|
+
```python
|
|
594
|
+
import sqlmodel_object_helpers as soh
|
|
595
|
+
|
|
596
|
+
# Physical columns — label, type, lookup derived automatically
|
|
597
|
+
table_meta, columns = await soh.build_dynamic_meta(
|
|
598
|
+
session,
|
|
599
|
+
EmailMessage,
|
|
600
|
+
name="email_messages",
|
|
601
|
+
columns=[
|
|
602
|
+
"id", # physical column — everything derived
|
|
603
|
+
"email_type_id", # FK on *_lkp → lookup_dict auto-derived
|
|
604
|
+
"updated_at",
|
|
605
|
+
soh.ColumnMeta( # virtual column — fully specified
|
|
606
|
+
json_path="last_editor.user_name",
|
|
607
|
+
label="Редактор",
|
|
608
|
+
type=soh.ColumnType.STRING,
|
|
609
|
+
),
|
|
610
|
+
],
|
|
611
|
+
role_type="operator", # per-role label/row_link overrides
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
# Returns (TableMeta, list[ColumnMeta]) ready for GetAllPagination
|
|
615
|
+
return soh.GetAllPagination(
|
|
616
|
+
table=table_meta,
|
|
617
|
+
columns=columns,
|
|
618
|
+
data=items,
|
|
619
|
+
pagination=pagination_r,
|
|
620
|
+
)
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
### Type derivation (SA type → ColumnType)
|
|
624
|
+
|
|
625
|
+
Physical column types are mapped automatically. Unknown types fall back to `string`.
|
|
626
|
+
|
|
627
|
+
| SQLAlchemy type | ColumnType |
|
|
628
|
+
|---|---|
|
|
629
|
+
| `Boolean` | `boolean` |
|
|
630
|
+
| `DateTime` | `datetime` |
|
|
631
|
+
| `Date` | `date` |
|
|
632
|
+
| `Integer`, `BigInteger`, `SmallInteger` | `integer` |
|
|
633
|
+
| `Numeric`, `Float` | `float` |
|
|
634
|
+
| `String`, `Text`, `Enum`, `Interval`, `ARRAY`, `Uuid` | `string` |
|
|
635
|
+
| Any other type | `string` (fallback) |
|
|
636
|
+
|
|
637
|
+
### Label precedence (per physical column)
|
|
638
|
+
|
|
639
|
+
1. `pg_description` — DBA edit via `COMMENT ON COLUMN` (supports per-role `||` overrides)
|
|
640
|
+
2. `Column(comment=...)` — Python-side default in the model
|
|
641
|
+
3. `ValueError` — fail-loud if both are missing
|
|
642
|
+
|
|
643
|
+
### Per-role overrides via `||` format
|
|
644
|
+
|
|
645
|
+
DBAs can set role-specific labels and row links in PostgreSQL comments:
|
|
646
|
+
|
|
647
|
+
```sql
|
|
648
|
+
COMMENT ON COLUMN emails.email_type_id IS 'Тип письма||operator=Категория||buh=Реквизит';
|
|
649
|
+
COMMENT ON TABLE emails IS 'Письма||row_link=/email/$id||row_link.operator=/op/email/$id';
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
### TTL cache
|
|
653
|
+
|
|
654
|
+
`pg_description` queries are cached per `(schema, table)` with a configurable TTL (default 60s):
|
|
655
|
+
|
|
656
|
+
```python
|
|
657
|
+
soh.configure_meta_cache_ttl(120) # change TTL to 120 seconds
|
|
658
|
+
soh.invalidate_meta_cache() # clear entire cache
|
|
659
|
+
soh.invalidate_meta_cache(schema="lead") # clear all entries for a schema
|
|
660
|
+
soh.invalidate_meta_cache("lead", "emails") # clear one entry
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Standalone mode
|
|
664
|
+
|
|
665
|
+
```python
|
|
666
|
+
import sqlmodel_object_helpers.standalone as soh_sa
|
|
667
|
+
|
|
668
|
+
table_meta, columns = await soh_sa.build_dynamic_meta(
|
|
669
|
+
EmailMessage,
|
|
670
|
+
name="email_messages",
|
|
671
|
+
columns=["id", "email_type_id"],
|
|
672
|
+
)
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### Backward compatibility and migration
|
|
676
|
+
|
|
677
|
+
`GetAllPagination` is fully backward compatible — `table` and `columns` default to `None`, so existing endpoints continue to work without changes:
|
|
678
|
+
|
|
679
|
+
```python
|
|
680
|
+
# Before (still works as-is)
|
|
681
|
+
return soh.GetAllPagination(data=items, pagination=pagination_r)
|
|
682
|
+
|
|
683
|
+
# After (meta added when ready)
|
|
684
|
+
return soh.GetAllPagination(
|
|
685
|
+
table=table_meta,
|
|
686
|
+
columns=columns,
|
|
687
|
+
data=items,
|
|
688
|
+
pagination=pagination_r,
|
|
689
|
+
)
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
Endpoints can be migrated one at a time. Three strategies per endpoint:
|
|
693
|
+
|
|
694
|
+
| Strategy | `table`/`columns` | Use case |
|
|
695
|
+
|---|---|---|
|
|
696
|
+
| **No meta** | Always `None` | Endpoint not yet migrated, frontend uses hardcoded table |
|
|
697
|
+
| **Always meta** | Sent on every request | Simple, no frontend caching logic needed |
|
|
698
|
+
| **Meta on first page** | Sent when `page=1`, `None` on pages 2+ | Saves traffic, frontend caches meta from first response |
|
|
699
|
+
|
|
588
700
|
## API Reference
|
|
589
701
|
|
|
590
702
|
### Session Management
|
|
@@ -595,7 +707,7 @@ result = await soh.get_objects(session, User, order_by=order)
|
|
|
595
707
|
|
|
596
708
|
### Standalone Wrappers
|
|
597
709
|
|
|
598
|
-
- `import sqlmodel_object_helpers.standalone as soh_sa` -- All 12 query/mutation functions without `session` parameter
|
|
710
|
+
- `import sqlmodel_object_helpers.standalone as soh_sa` -- All 12 query/mutation functions plus `build_dynamic_meta` without `session` parameter
|
|
599
711
|
|
|
600
712
|
### Settings
|
|
601
713
|
|
|
@@ -620,6 +732,14 @@ result = await soh.get_objects(session, User, order_by=order)
|
|
|
620
732
|
- `soh.delete_objects(session, model, filters)` -- Bulk delete via single `DELETE ... WHERE`
|
|
621
733
|
- `soh.check_for_related_records(session, model, pk)` -- Pre-deletion dependency check
|
|
622
734
|
|
|
735
|
+
### Dynamic Meta
|
|
736
|
+
|
|
737
|
+
- `soh.build_dynamic_meta(session, model, *, name, columns, header, row_link, role_type)` -- Build `TableMeta` + `list[ColumnMeta]` from model + `pg_description`
|
|
738
|
+
- `soh.load_pg_comments(session, schema, table)` -- Read `(table_comment, {col: comment})` from `pg_description` (TTL-cached)
|
|
739
|
+
- `soh.configure_meta_cache_ttl(seconds)` -- Override default `pg_description` cache TTL (default: 60s)
|
|
740
|
+
- `soh.invalidate_meta_cache(schema, table)` -- Manually invalidate the `pg_description` cache (full, by-schema, or by-table)
|
|
741
|
+
- `soh.ColumnEntry` -- Type alias: `str | ColumnMeta` (element of `columns` list in `build_dynamic_meta`)
|
|
742
|
+
|
|
623
743
|
### Filter Builders
|
|
624
744
|
|
|
625
745
|
- `soh.build_filter(model, filters, ...)` -- Build SQLAlchemy expression from LogicalFilter dict
|
|
@@ -728,6 +848,10 @@ This project follows [Semantic Versioning](https://semver.org/) (MAJOR.MINOR.PAT
|
|
|
728
848
|
|
|
729
849
|
## Changelog
|
|
730
850
|
|
|
851
|
+
### 0.0.6
|
|
852
|
+
|
|
853
|
+
- **build_dynamic_meta** / **load_pg_comments** / **configure_meta_cache_ttl** / **invalidate_meta_cache** — dynamic UI metadata reader. Builds `TableMeta` and `list[ColumnMeta]` from a SQLModel class by reading PostgreSQL `pg_description` (TTL-cached, default 60s) with fallback to `Column(comment=...)` defaults from the model. Supports per-role label/row_link overrides via extended `||` comment format. For physical columns the label precedence is `pg_description` > `Column(comment=...)` model default. For virtual columns (paths with `.` traversing relationships) and full overrides — pass a fully-specified `ColumnMeta` instance directly in the `columns` list (used as-is, no derivation). Lookup `lookup_dict`/`lookup_path` are derived from FK on `*_lkp` tables by `{schema}_{base}` convention. Type derived from SA column type. DBAs can edit labels via `COMMENT ON COLUMN/TABLE` SQL — frontend reflects changes within cache TTL without redeploy. No sync block, no schema migrations introduced; the application's `db.py` is not touched.
|
|
854
|
+
|
|
731
855
|
### 0.0.5
|
|
732
856
|
|
|
733
857
|
- **FilterDatetimeRange** / **FilterNaiveDatetimeRange** — range filters that parse comma-separated date strings (`"2026-04-01,2026-05-06"`) into `gt`/`lt` operators for SQL filtering
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
"""sqlmodel-object-helpers — reusable query helpers for SQLModel projects."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.0.
|
|
3
|
+
__version__ = "0.0.6"
|
|
4
4
|
|
|
5
5
|
from .constants import QueryHelperSettings, settings
|
|
6
|
+
from .dynamic_meta import (
|
|
7
|
+
ColumnEntry,
|
|
8
|
+
build_dynamic_meta,
|
|
9
|
+
configure_meta_cache_ttl,
|
|
10
|
+
invalidate_meta_cache,
|
|
11
|
+
load_pg_comments,
|
|
12
|
+
)
|
|
6
13
|
from .exceptions import (
|
|
7
14
|
DatabaseError,
|
|
8
15
|
InvalidFilterError,
|
|
@@ -13,7 +20,6 @@ from .exceptions import (
|
|
|
13
20
|
)
|
|
14
21
|
from .filters import build_filter, build_flat_filter, flatten_filters
|
|
15
22
|
from .loaders import build_load_chain, build_load_options
|
|
16
|
-
from .operators import SPECIAL_OPERATORS, SUPPORTED_OPERATORS, Operator
|
|
17
23
|
from .mutations import (
|
|
18
24
|
add_object,
|
|
19
25
|
add_objects,
|
|
@@ -23,8 +29,10 @@ from .mutations import (
|
|
|
23
29
|
update_object,
|
|
24
30
|
update_objects,
|
|
25
31
|
)
|
|
32
|
+
from .operators import SPECIAL_OPERATORS, SUPPORTED_OPERATORS, Operator
|
|
26
33
|
from .query import count_objects, exists_object, get_object, get_objects, get_projection
|
|
27
34
|
from .session import auto_session, configure, create_session_dependency
|
|
35
|
+
from .types.columns import BoolLabels, ColumnMeta, ColumnType, TableMeta
|
|
28
36
|
from .types.datetime import UTCDatetime
|
|
29
37
|
from .types.filters import (
|
|
30
38
|
FilterBool,
|
|
@@ -43,7 +51,6 @@ from .types.filters import (
|
|
|
43
51
|
OrderDesc,
|
|
44
52
|
TimeFilter,
|
|
45
53
|
)
|
|
46
|
-
from .types.columns import BoolLabels, ColumnMeta, ColumnType, TableMeta
|
|
47
54
|
from .types.pagination import (
|
|
48
55
|
GetAllPagination,
|
|
49
56
|
LookupMeta,
|
|
@@ -57,16 +64,19 @@ __all__ = [
|
|
|
57
64
|
"add_object",
|
|
58
65
|
"add_objects",
|
|
59
66
|
"auto_session",
|
|
67
|
+
"build_dynamic_meta",
|
|
60
68
|
"build_filter",
|
|
61
69
|
"build_flat_filter",
|
|
62
70
|
"build_load_chain",
|
|
63
71
|
"build_load_options",
|
|
64
72
|
"check_for_related_records",
|
|
65
73
|
"BoolLabels",
|
|
74
|
+
"ColumnEntry",
|
|
66
75
|
"ColumnMeta",
|
|
67
76
|
"ColumnSpec",
|
|
68
77
|
"ColumnType",
|
|
69
78
|
"configure",
|
|
79
|
+
"configure_meta_cache_ttl",
|
|
70
80
|
"count_objects",
|
|
71
81
|
"create_session_dependency",
|
|
72
82
|
"DatabaseError",
|
|
@@ -88,8 +98,10 @@ __all__ = [
|
|
|
88
98
|
"get_objects",
|
|
89
99
|
"get_projection",
|
|
90
100
|
"GetAllPagination",
|
|
101
|
+
"invalidate_meta_cache",
|
|
91
102
|
"InvalidFilterError",
|
|
92
103
|
"InvalidLoadPathError",
|
|
104
|
+
"load_pg_comments",
|
|
93
105
|
"LogicalFilter",
|
|
94
106
|
"LookupMeta",
|
|
95
107
|
"LookupResponse",
|