sqlmodel-ext 0.4.2__tar.gz → 0.5.1__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_ext-0.4.2 → sqlmodel_ext-0.5.1}/.github/workflows/ci.yml +146 -100
- sqlmodel_ext-0.5.1/CHANGELOG.md +263 -0
- sqlmodel_ext-0.5.1/PKG-INFO +1934 -0
- sqlmodel_ext-0.5.1/README.md +1878 -0
- sqlmodel_ext-0.5.1/README_zh.md +1878 -0
- sqlmodel_ext-0.5.1/ai-rules/AGENTS.md +286 -0
- sqlmodel_ext-0.5.1/ai-rules/CLAUDE.md +6 -0
- sqlmodel_ext-0.5.1/ai-rules/README.md +58 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/en.mts +126 -88
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/zh.mts +126 -88
- sqlmodel_ext-0.5.1/docs/en/explanation/cached-table.md +189 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/cascade-delete-semantics.md +155 -153
- sqlmodel_ext-0.5.1/docs/en/explanation/crud-pipeline.md +279 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/index.md +48 -41
- sqlmodel_ext-0.5.1/docs/en/explanation/metaclass.md +358 -0
- sqlmodel_ext-0.5.1/docs/en/explanation/optimistic-lock.md +160 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/polymorphic-internals.md +242 -234
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/prerequisites.md +187 -158
- sqlmodel_ext-0.5.1/docs/en/explanation/relation-load-checker.md +184 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/relation-preload.md +224 -197
- sqlmodel_ext-0.5.1/docs/en/explanation/single-source-of-truth.md +138 -0
- sqlmodel_ext-0.5.1/docs/en/explanation/transactional-cache-transparency.md +145 -0
- sqlmodel_ext-0.5.1/docs/en/explanation/unset-three-state.md +317 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/cached-table.md +17 -17
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/crud.md +15 -15
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/field-types.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/index.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/optimistic-lock.md +14 -14
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/pagination.md +14 -14
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/polymorphic.md +17 -17
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/relation-load-checker.md +16 -16
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/relation-preload.md +17 -17
- sqlmodel_ext-0.5.1/docs/en/how-to/aggregate-queries.md +133 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/cache-queries.md +177 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/check-partial-dtos.md +175 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/configure-cascade-delete.md +234 -234
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/define-jti-models.md +122 -120
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/define-sti-models.md +15 -4
- sqlmodel_ext-0.5.1/docs/en/how-to/enforce-locking-and-isolation.md +227 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/extra-mixins.md +256 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/handle-concurrent-updates.md +123 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/handle-referenced-deletes.md +86 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/index.md +57 -41
- sqlmodel_ext-0.5.1/docs/en/how-to/integrate-with-fastapi.md +179 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/keyset-pagination.md +102 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/migrate-to-0-5.md +319 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/paginate-a-list-endpoint.md +102 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/prevent-missing-greenlet.md +45 -2
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/release-connection-during-long-io.md +183 -181
- sqlmodel_ext-0.5.1/docs/en/how-to/type-check-with-basedpyright.md +258 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/use-with-ai-assistants.md +102 -0
- sqlmodel_ext-0.5.1/docs/en/how-to/write-patch-endpoints.md +223 -0
- sqlmodel_ext-0.5.1/docs/en/index.md +76 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/cached-table.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/crud.md +15 -15
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/index.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/metaclass.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/optimistic-lock.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/polymorphic.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/prerequisites.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/relation-load-checker.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/relation-preload.md +13 -13
- sqlmodel_ext-0.5.1/docs/en/reference/base-classes.md +225 -0
- sqlmodel_ext-0.5.1/docs/en/reference/crud-methods.md +392 -0
- sqlmodel_ext-0.5.1/docs/en/reference/decorators.md +260 -0
- sqlmodel_ext-0.5.1/docs/en/reference/field-types.md +410 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/reference/index.md +54 -54
- sqlmodel_ext-0.5.1/docs/en/reference/mixins.md +404 -0
- sqlmodel_ext-0.5.1/docs/en/reference/pagination-types.md +178 -0
- sqlmodel_ext-0.5.1/docs/en/tutorials/01-getting-started.md +263 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/tutorials/02-building-a-blog-api.md +45 -15
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/tutorials/03-adding-redis-cache.md +22 -11
- sqlmodel_ext-0.5.1/docs/explanation/cached-table.md +189 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/cascade-delete-semantics.md +155 -153
- sqlmodel_ext-0.5.1/docs/explanation/crud-pipeline.md +279 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/index.md +48 -41
- sqlmodel_ext-0.5.1/docs/explanation/metaclass.md +358 -0
- sqlmodel_ext-0.5.1/docs/explanation/optimistic-lock.md +160 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/polymorphic-internals.md +231 -223
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/prerequisites.md +187 -158
- sqlmodel_ext-0.5.1/docs/explanation/relation-load-checker.md +184 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/relation-preload.md +224 -197
- sqlmodel_ext-0.5.1/docs/explanation/single-source-of-truth.md +138 -0
- sqlmodel_ext-0.5.1/docs/explanation/transactional-cache-transparency.md +145 -0
- sqlmodel_ext-0.5.1/docs/explanation/unset-three-state.md +317 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/cached-table.md +17 -17
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/crud.md +15 -15
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/field-types.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/index.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/optimistic-lock.md +14 -14
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/pagination.md +14 -14
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/polymorphic.md +17 -17
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/relation-load-checker.md +16 -16
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/relation-preload.md +17 -17
- sqlmodel_ext-0.5.1/docs/how-to/aggregate-queries.md +133 -0
- sqlmodel_ext-0.5.1/docs/how-to/cache-queries.md +177 -0
- sqlmodel_ext-0.5.1/docs/how-to/check-partial-dtos.md +175 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/configure-cascade-delete.md +234 -234
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/define-jti-models.md +122 -120
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/define-sti-models.md +15 -4
- sqlmodel_ext-0.5.1/docs/how-to/enforce-locking-and-isolation.md +227 -0
- sqlmodel_ext-0.5.1/docs/how-to/extra-mixins.md +256 -0
- sqlmodel_ext-0.5.1/docs/how-to/handle-concurrent-updates.md +123 -0
- sqlmodel_ext-0.5.1/docs/how-to/handle-referenced-deletes.md +86 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/index.md +57 -41
- sqlmodel_ext-0.5.1/docs/how-to/integrate-with-fastapi.md +179 -0
- sqlmodel_ext-0.5.1/docs/how-to/keyset-pagination.md +102 -0
- sqlmodel_ext-0.5.1/docs/how-to/migrate-to-0-5.md +319 -0
- sqlmodel_ext-0.5.1/docs/how-to/paginate-a-list-endpoint.md +102 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/prevent-missing-greenlet.md +45 -2
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/release-connection-during-long-io.md +183 -181
- sqlmodel_ext-0.5.1/docs/how-to/type-check-with-basedpyright.md +258 -0
- sqlmodel_ext-0.5.1/docs/how-to/use-with-ai-assistants.md +102 -0
- sqlmodel_ext-0.5.1/docs/how-to/write-patch-endpoints.md +223 -0
- sqlmodel_ext-0.5.1/docs/index.md +76 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/cached-table.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/crud.md +15 -15
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/index.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/metaclass.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/optimistic-lock.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/polymorphic.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/prerequisites.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/relation-load-checker.md +13 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/relation-preload.md +13 -13
- sqlmodel_ext-0.5.1/docs/reference/base-classes.md +224 -0
- sqlmodel_ext-0.5.1/docs/reference/crud-methods.md +392 -0
- sqlmodel_ext-0.5.1/docs/reference/decorators.md +260 -0
- sqlmodel_ext-0.5.1/docs/reference/field-types.md +410 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/reference/index.md +54 -54
- sqlmodel_ext-0.5.1/docs/reference/mixins.md +404 -0
- sqlmodel_ext-0.5.1/docs/reference/pagination-types.md +178 -0
- sqlmodel_ext-0.5.1/docs/tutorials/01-getting-started.md +263 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/tutorials/02-building-a-blog-api.md +45 -14
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/tutorials/03-adding-redis-cache.md +22 -11
- sqlmodel_ext-0.5.1/examples/01_unset_patch.py +195 -0
- sqlmodel_ext-0.5.1/examples/02_single_source_types.py +104 -0
- sqlmodel_ext-0.5.1/examples/03_keyset_pagination.py +96 -0
- sqlmodel_ext-0.5.1/examples/04_locking_and_isolation.py +155 -0
- sqlmodel_ext-0.5.1/examples/05_aggregates.py +91 -0
- sqlmodel_ext-0.5.1/examples/06_referenced_delete.py +118 -0
- sqlmodel_ext-0.5.1/examples/07_transactional_cache.py +108 -0
- sqlmodel_ext-0.5.1/examples/08_optimistic_lock.py +98 -0
- sqlmodel_ext-0.5.1/examples/09_relation_preload_bulk.py +117 -0
- sqlmodel_ext-0.5.1/examples/10_typed_select.py +103 -0
- sqlmodel_ext-0.5.1/examples/11_type_errors_caught_by_basedpyright.py +80 -0
- sqlmodel_ext-0.5.1/examples/README.md +154 -0
- sqlmodel_ext-0.5.1/examples/check_derived_demo/pyrightconfig.json +4 -0
- sqlmodel_ext-0.5.1/examples/check_derived_demo/shop/__init__.py +7 -0
- sqlmodel_ext-0.5.1/examples/check_derived_demo/shop/handlers.py +16 -0
- sqlmodel_ext-0.5.1/examples/check_derived_demo/shop/models.py +29 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/examples/consumer_integration/run.py +29 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/pyproject.toml +18 -3
- sqlmodel_ext-0.5.1/pyrightconfig.json +48 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/__init__.py +254 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/_compat.py +4 -2
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/_exceptions.py +1 -1
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/_sa_type.py +20 -8
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/_type_unwrap.py +117 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/base.py +963 -302
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/check_derived.py +565 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/constants.py +76 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/dependencies.py +178 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/derived_decls.py +818 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/field_types/__init__.py +997 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/_internal/path.py +24 -12
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/postgresql/__init__.py +16 -7
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/postgresql/array.py +232 -218
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/field_types/dialects/postgresql/jsonb_types.py +266 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/postgresql/numpy_vector.py +43 -27
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/field_types/ip_address.py +86 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/field_types/mixins/__init__.py +1 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/mixins/module_name_mixin.py +2 -2
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/url.py +6 -6
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/__init__.py +79 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/_uuid.py +69 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/mixins/cached_table.py +969 -391
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/constants.py +17 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/exceptions.py +84 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/migration_cache_invalidation.py +243 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/mixin_table_scan.py +96 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/optimistic_lock.py +161 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/mixins/polymorphic.py +84 -38
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/relation_preload.py +916 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/resource_quota.py +318 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/mixins/table.py +866 -162
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/trgm_searchable.py +230 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/pagination.py +291 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/relation_load_checker.py +6367 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/select.py +466 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/session.py +633 -0
- sqlmodel_ext-0.5.1/src/sqlmodel_ext/unset.py +189 -0
- sqlmodel_ext-0.5.1/tests/__init__.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/conftest.py +58 -58
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_annotated_default_factory_inheritance.py +307 -194
- sqlmodel_ext-0.5.1/tests/test_cached_commit_paths.py +265 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_cached_table.py +239 -7
- sqlmodel_ext-0.5.1/tests/test_check_derived.py +239 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_crud_extended.py +244 -4
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_decimal_types.py +431 -256
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_deferred_index.py +50 -50
- sqlmodel_ext-0.5.1/tests/test_derived_decls.py +159 -0
- sqlmodel_ext-0.5.1/tests/test_enhanced_session.py +526 -0
- sqlmodel_ext-0.5.1/tests/test_examples.py +52 -0
- sqlmodel_ext-0.5.1/tests/test_field_types_aliases_extra.py +292 -0
- sqlmodel_ext-0.5.1/tests/test_field_types_jsonb_limits.py +186 -0
- sqlmodel_ext-0.5.1/tests/test_final_051_rereview.py +52 -0
- sqlmodel_ext-0.5.1/tests/test_final_051_review.py +262 -0
- sqlmodel_ext-0.5.1/tests/test_final_051_review2.py +72 -0
- sqlmodel_ext-0.5.1/tests/test_final_check_derived_review2.py +91 -0
- sqlmodel_ext-0.5.1/tests/test_final_check_derived_temp_root.py +40 -0
- sqlmodel_ext-0.5.1/tests/test_final_nested_duplicate_names.py +38 -0
- sqlmodel_ext-0.5.1/tests/test_final_omitted_sentinel_alias_modes.py +38 -0
- sqlmodel_ext-0.5.1/tests/test_final_partial_alias_generator.py +20 -0
- sqlmodel_ext-0.5.1/tests/test_final_partial_annotated_field_warnings.py +28 -0
- sqlmodel_ext-0.5.1/tests/test_final_partial_discriminator.py +42 -0
- sqlmodel_ext-0.5.1/tests/test_final_partial_frozen_field.py +21 -0
- sqlmodel_ext-0.5.1/tests/test_final_partial_rhs_field_metadata.py +36 -0
- sqlmodel_ext-0.5.1/tests/test_final_review4_followup.py +109 -0
- sqlmodel_ext-0.5.1/tests/test_final_review5.py +145 -0
- sqlmodel_ext-0.5.1/tests/test_inherited_column_attributes.py +666 -0
- sqlmodel_ext-0.5.1/tests/test_metaclass_extra.py +396 -0
- sqlmodel_ext-0.5.1/tests/test_mixins_extra.py +323 -0
- sqlmodel_ext-0.5.1/tests/test_optimistic_lock.py +243 -0
- sqlmodel_ext-0.5.1/tests/test_pagination.py +492 -0
- sqlmodel_ext-0.5.1/tests/test_partial.py +326 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_pg_dialect_types.py +18 -13
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_polymorphic_jti.py +39 -3
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_polymorphic_sti_query.py +55 -1
- sqlmodel_ext-0.5.1/tests/test_query_dependency.py +326 -0
- sqlmodel_ext-0.5.1/tests/test_query_dependency_metadata.py +154 -0
- sqlmodel_ext-0.5.1/tests/test_relation_load_checker_load_resolution.py +1555 -0
- sqlmodel_ext-0.5.1/tests/test_relation_load_checker_sessions.py +563 -0
- sqlmodel_ext-0.5.1/tests/test_relation_preload.py +499 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_relationship_pep604.py +115 -115
- sqlmodel_ext-0.5.1/tests/test_review_mixed_version_cache.py +59 -0
- sqlmodel_ext-0.5.1/tests/test_review_raw_insert_cache.py +50 -0
- sqlmodel_ext-0.5.1/tests/test_review_raw_insert_edges.py +68 -0
- sqlmodel_ext-0.5.1/tests/test_review_raw_insert_late_model.py +29 -0
- sqlmodel_ext-0.5.1/tests/test_review_raw_insert_sti.py +56 -0
- sqlmodel_ext-0.5.1/tests/test_select.py +156 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_smoke_imports.py +265 -157
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_sti_default_isolation.py +132 -132
- sqlmodel_ext-0.5.1/tests/test_unset.py +241 -0
- sqlmodel_ext-0.4.2/PKG-INFO +0 -1518
- sqlmodel_ext-0.4.2/README.md +0 -1467
- sqlmodel_ext-0.4.2/README_zh.md +0 -1383
- sqlmodel_ext-0.4.2/docs/en/explanation/cached-table.md +0 -248
- sqlmodel_ext-0.4.2/docs/en/explanation/crud-pipeline.md +0 -288
- sqlmodel_ext-0.4.2/docs/en/explanation/metaclass.md +0 -375
- sqlmodel_ext-0.4.2/docs/en/explanation/optimistic-lock.md +0 -120
- sqlmodel_ext-0.4.2/docs/en/explanation/relation-load-checker.md +0 -148
- sqlmodel_ext-0.4.2/docs/en/how-to/cache-queries.md +0 -143
- sqlmodel_ext-0.4.2/docs/en/how-to/handle-concurrent-updates.md +0 -94
- sqlmodel_ext-0.4.2/docs/en/how-to/integrate-with-fastapi.md +0 -119
- sqlmodel_ext-0.4.2/docs/en/how-to/paginate-a-list-endpoint.md +0 -79
- sqlmodel_ext-0.4.2/docs/en/index.md +0 -45
- sqlmodel_ext-0.4.2/docs/en/reference/base-classes.md +0 -143
- sqlmodel_ext-0.4.2/docs/en/reference/crud-methods.md +0 -281
- sqlmodel_ext-0.4.2/docs/en/reference/decorators.md +0 -164
- sqlmodel_ext-0.4.2/docs/en/reference/field-types.md +0 -279
- sqlmodel_ext-0.4.2/docs/en/reference/mixins.md +0 -257
- sqlmodel_ext-0.4.2/docs/en/reference/pagination-types.md +0 -113
- sqlmodel_ext-0.4.2/docs/en/tutorials/01-getting-started.md +0 -175
- sqlmodel_ext-0.4.2/docs/explanation/cached-table.md +0 -244
- sqlmodel_ext-0.4.2/docs/explanation/crud-pipeline.md +0 -288
- sqlmodel_ext-0.4.2/docs/explanation/metaclass.md +0 -351
- sqlmodel_ext-0.4.2/docs/explanation/optimistic-lock.md +0 -120
- sqlmodel_ext-0.4.2/docs/explanation/relation-load-checker.md +0 -136
- sqlmodel_ext-0.4.2/docs/how-to/cache-queries.md +0 -143
- sqlmodel_ext-0.4.2/docs/how-to/handle-concurrent-updates.md +0 -93
- sqlmodel_ext-0.4.2/docs/how-to/integrate-with-fastapi.md +0 -119
- sqlmodel_ext-0.4.2/docs/how-to/paginate-a-list-endpoint.md +0 -79
- sqlmodel_ext-0.4.2/docs/index.md +0 -45
- sqlmodel_ext-0.4.2/docs/reference/base-classes.md +0 -140
- sqlmodel_ext-0.4.2/docs/reference/crud-methods.md +0 -279
- sqlmodel_ext-0.4.2/docs/reference/decorators.md +0 -164
- sqlmodel_ext-0.4.2/docs/reference/field-types.md +0 -279
- sqlmodel_ext-0.4.2/docs/reference/mixins.md +0 -252
- sqlmodel_ext-0.4.2/docs/reference/pagination-types.md +0 -113
- sqlmodel_ext-0.4.2/docs/tutorials/01-getting-started.md +0 -175
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/__init__.py +0 -173
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/field_types/__init__.py +0 -560
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/field_types/dialects/postgresql/jsonb_types.py +0 -178
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/field_types/ip_address.py +0 -50
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/field_types/mixins/__init__.py +0 -1
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/mixins/__init__.py +0 -39
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/mixins/optimistic_lock.py +0 -85
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/mixins/relation_preload.py +0 -436
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/pagination.py +0 -127
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/relation_load_checker.py +0 -3825
- sqlmodel_ext-0.4.2/src/sqlmodel_ext/session.py +0 -105
- sqlmodel_ext-0.4.2/tests/test_all_fields_optional_constraints.py +0 -76
- sqlmodel_ext-0.4.2/tests/test_enhanced_session.py +0 -87
- sqlmodel_ext-0.4.2/tests/test_metaclass_extra.py +0 -194
- sqlmodel_ext-0.4.2/tests/test_optimistic_lock.py +0 -190
- sqlmodel_ext-0.4.2/tests/test_pagination.py +0 -259
- sqlmodel_ext-0.4.2/tests/test_relation_preload.py +0 -240
- sqlmodel_ext-0.4.2/uv.lock +0 -585
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/.gitignore +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/LICENSE +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/config.mts +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/theme/index.ts +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/theme/style/custom.css +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/tutorials/index.md +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/tutorials/index.md +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/package.json +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/pnpm-lock.yaml +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/_utils.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/_internal/__init__.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/_ssrf.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/__init__.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/postgresql/exceptions.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/mixins/info_response.py +0 -0
- /sqlmodel_ext-0.4.2/tests/__init__.py → /sqlmodel_ext-0.5.1/src/sqlmodel_ext/py.typed +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/_models.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_array_explicit_field_sa_type.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_basic_crud.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_field_types_misc.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_field_types_str_num.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_field_types_url_ssrf.py +0 -0
- {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_relation_load_checker.py +0 -0
|
@@ -1,100 +1,146 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [master]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [master]
|
|
8
|
-
workflow_dispatch:
|
|
9
|
-
|
|
10
|
-
permissions:
|
|
11
|
-
contents: read
|
|
12
|
-
|
|
13
|
-
concurrency:
|
|
14
|
-
group: ci-${{ github.ref }}
|
|
15
|
-
cancel-in-progress: true
|
|
16
|
-
|
|
17
|
-
jobs:
|
|
18
|
-
# -------------------------------------------------------------------
|
|
19
|
-
# Job 1: source-tree tests across supported Python versions.
|
|
20
|
-
# Uses uv sync so the lockfile is honoured and runs are reproducible.
|
|
21
|
-
# -------------------------------------------------------------------
|
|
22
|
-
test:
|
|
23
|
-
name: test (py${{ matrix.python-version }})
|
|
24
|
-
runs-on: ubuntu-latest
|
|
25
|
-
strategy:
|
|
26
|
-
fail-fast: false
|
|
27
|
-
matrix:
|
|
28
|
-
python-version: ["3.12", "3.13", "3.14"]
|
|
29
|
-
steps:
|
|
30
|
-
- name: Checkout
|
|
31
|
-
uses: actions/checkout@v4
|
|
32
|
-
|
|
33
|
-
- name: Install uv
|
|
34
|
-
uses: astral-sh/setup-uv@v5
|
|
35
|
-
with:
|
|
36
|
-
enable-cache: true
|
|
37
|
-
cache-dependency-glob: "uv.lock"
|
|
38
|
-
|
|
39
|
-
- name: Set up Python ${{ matrix.python-version }}
|
|
40
|
-
run: uv python install ${{ matrix.python-version }}
|
|
41
|
-
|
|
42
|
-
- name: Create venv and install project with dev extras
|
|
43
|
-
# Dev extras: pytest, pytest-asyncio, aiosqlite, ruff. Editable install
|
|
44
|
-
# keeps the wheel build separate for the consumer-integration job.
|
|
45
|
-
run: |
|
|
46
|
-
uv venv --python ${{ matrix.python-version }} .venv
|
|
47
|
-
uv pip install --python .venv/bin/python -e ".[dev]"
|
|
48
|
-
|
|
49
|
-
- name: Run pytest
|
|
50
|
-
run: .venv/bin/python -m pytest tests/ -v
|
|
51
|
-
|
|
52
|
-
# -------------------------------------------------------------------
|
|
53
|
-
# Job
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
steps:
|
|
66
|
-
- name: Checkout
|
|
67
|
-
uses: actions/checkout@v4
|
|
68
|
-
|
|
69
|
-
- name: Install uv
|
|
70
|
-
uses: astral-sh/setup-uv@v5
|
|
71
|
-
with:
|
|
72
|
-
enable-cache: true
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
- name:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [master]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ci-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
# -------------------------------------------------------------------
|
|
19
|
+
# Job 1: source-tree tests across supported Python versions.
|
|
20
|
+
# Uses uv sync so the lockfile is honoured and runs are reproducible.
|
|
21
|
+
# -------------------------------------------------------------------
|
|
22
|
+
test:
|
|
23
|
+
name: test (py${{ matrix.python-version }})
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
strategy:
|
|
26
|
+
fail-fast: false
|
|
27
|
+
matrix:
|
|
28
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
29
|
+
steps:
|
|
30
|
+
- name: Checkout
|
|
31
|
+
uses: actions/checkout@v4
|
|
32
|
+
|
|
33
|
+
- name: Install uv
|
|
34
|
+
uses: astral-sh/setup-uv@v5
|
|
35
|
+
with:
|
|
36
|
+
enable-cache: true
|
|
37
|
+
cache-dependency-glob: "uv.lock"
|
|
38
|
+
|
|
39
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
40
|
+
run: uv python install ${{ matrix.python-version }}
|
|
41
|
+
|
|
42
|
+
- name: Create venv and install project with dev extras
|
|
43
|
+
# Dev extras: pytest, pytest-asyncio, aiosqlite, ruff. Editable install
|
|
44
|
+
# keeps the wheel build separate for the consumer-integration job.
|
|
45
|
+
run: |
|
|
46
|
+
uv venv --python ${{ matrix.python-version }} .venv
|
|
47
|
+
uv pip install --python .venv/bin/python -e ".[dev]"
|
|
48
|
+
|
|
49
|
+
- name: Run pytest
|
|
50
|
+
run: .venv/bin/python -m pytest tests/ -v
|
|
51
|
+
|
|
52
|
+
# -------------------------------------------------------------------
|
|
53
|
+
# Job 1b: static type check. basedpyright reads pyrightconfig.json from
|
|
54
|
+
# the repository root (src only, pythonVersion 3.12 = lowest supported).
|
|
55
|
+
# The gate is 0 errors and 0 warnings: pyrightconfig.json sets failOnWarnings,
|
|
56
|
+
# so any warning makes basedpyright exit non-zero and fails the job.
|
|
57
|
+
# The alembic extra is installed so the optional-import module
|
|
58
|
+
# `mixins/migration_cache_invalidation.py` resolves its imports.
|
|
59
|
+
# "0 errors" is also what an empty scan prints, so the job additionally
|
|
60
|
+
# fails when the verbose log does not report a non-zero source-file count.
|
|
61
|
+
# -------------------------------------------------------------------
|
|
62
|
+
typecheck:
|
|
63
|
+
name: basedpyright (py3.12)
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- name: Checkout
|
|
67
|
+
uses: actions/checkout@v4
|
|
68
|
+
|
|
69
|
+
- name: Install uv
|
|
70
|
+
uses: astral-sh/setup-uv@v5
|
|
71
|
+
with:
|
|
72
|
+
enable-cache: true
|
|
73
|
+
cache-dependency-glob: "uv.lock"
|
|
74
|
+
|
|
75
|
+
- name: Set up Python 3.12
|
|
76
|
+
run: uv python install 3.12
|
|
77
|
+
|
|
78
|
+
- name: Create venv and install project with dev extras
|
|
79
|
+
run: |
|
|
80
|
+
uv venv --python 3.12 .venv
|
|
81
|
+
uv pip install --python .venv/bin/python -e ".[dev,alembic]"
|
|
82
|
+
|
|
83
|
+
- name: Run basedpyright
|
|
84
|
+
# Activate the venv so basedpyright resolves imports against it.
|
|
85
|
+
run: |
|
|
86
|
+
source .venv/bin/activate
|
|
87
|
+
status=0
|
|
88
|
+
basedpyright --verbose > basedpyright.log 2>&1 || status=$?
|
|
89
|
+
grep -E " - (error|warning)" basedpyright.log || true
|
|
90
|
+
grep -E "^[0-9]+ errors?, [0-9]+ warnings?" basedpyright.log
|
|
91
|
+
grep -E "^Found [0-9]+ source files" basedpyright.log
|
|
92
|
+
if ! grep -qE "^Found [1-9][0-9]* source files" basedpyright.log; then
|
|
93
|
+
echo "::error::basedpyright scanned no source files (check pyrightconfig.json include/exclude)"
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
exit $status
|
|
97
|
+
|
|
98
|
+
# -------------------------------------------------------------------
|
|
99
|
+
# Job 2: build wheel + sdist, then install the wheel into a clean
|
|
100
|
+
# virtualenv with zero dev deps and run the consumer integration
|
|
101
|
+
# script. This simulates an external PyPI consumer.
|
|
102
|
+
# -------------------------------------------------------------------
|
|
103
|
+
consumer-integration:
|
|
104
|
+
name: consumer integration (py${{ matrix.python-version }})
|
|
105
|
+
runs-on: ubuntu-latest
|
|
106
|
+
needs: test
|
|
107
|
+
strategy:
|
|
108
|
+
fail-fast: false
|
|
109
|
+
matrix:
|
|
110
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
111
|
+
steps:
|
|
112
|
+
- name: Checkout
|
|
113
|
+
uses: actions/checkout@v4
|
|
114
|
+
|
|
115
|
+
- name: Install uv
|
|
116
|
+
uses: astral-sh/setup-uv@v5
|
|
117
|
+
with:
|
|
118
|
+
enable-cache: true
|
|
119
|
+
|
|
120
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
121
|
+
run: uv python install ${{ matrix.python-version }}
|
|
122
|
+
|
|
123
|
+
- name: Build wheel and sdist
|
|
124
|
+
run: uv tool run --from build python -m build
|
|
125
|
+
|
|
126
|
+
- name: Create isolated consumer venv
|
|
127
|
+
run: uv venv --python ${{ matrix.python-version }} /tmp/consumer-env
|
|
128
|
+
|
|
129
|
+
- name: Install built wheel (no dev deps, only the wheel + aiosqlite driver)
|
|
130
|
+
run: uv pip install --python /tmp/consumer-env/bin/python dist/*.whl aiosqlite
|
|
131
|
+
|
|
132
|
+
- name: Confirm installed version matches wheel
|
|
133
|
+
run: |
|
|
134
|
+
/tmp/consumer-env/bin/python -c "import sqlmodel_ext; print('installed:', sqlmodel_ext.__version__ if hasattr(sqlmodel_ext, '__version__') else '(no __version__)')"
|
|
135
|
+
|
|
136
|
+
- name: Run consumer integration scenario
|
|
137
|
+
run: /tmp/consumer-env/bin/python examples/consumer_integration/run.py
|
|
138
|
+
|
|
139
|
+
- name: Upload distribution artifacts
|
|
140
|
+
if: matrix.python-version == '3.12'
|
|
141
|
+
uses: actions/upload-artifact@v4
|
|
142
|
+
with:
|
|
143
|
+
name: dist
|
|
144
|
+
path: dist/
|
|
145
|
+
if-no-files-found: error
|
|
146
|
+
retention-days: 14
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
|
+
|
|
6
|
+
> **The project is still WIP / alpha.** Any release may contain breaking changes;
|
|
7
|
+
> there is no stability or backward-compatibility guarantee between versions, and
|
|
8
|
+
> you use it at your own risk. Pin the exact version you depend on.
|
|
9
|
+
|
|
10
|
+
## [0.5.1]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`query_dependency(Model)`**: FastAPI dependency for query-parameter DTOs
|
|
15
|
+
(`TableViewRequest`, `PaginationRequest`, `PageWindowRequest`, `TimeFilterRequest`,
|
|
16
|
+
`TrgmSearchRequest`, your subclasses):
|
|
17
|
+
`Annotated[TableViewRequest, Depends(query_dependency(TableViewRequest))]`. It declares
|
|
18
|
+
one query parameter per field carrying the field's type, constraints, default, `title`,
|
|
19
|
+
description, `examples`, `deprecated`, `json_schema_extra` and `discriminator` (each
|
|
20
|
+
parameter's OpenAPI schema is the field's JSON schema; a callable `json_schema_extra`
|
|
21
|
+
or a `Discriminator` object is a `TypeError`), builds the model itself and
|
|
22
|
+
re-raises a failed construction as `RequestValidationError` with locations prefixed by
|
|
23
|
+
`'query'`, so FastAPI answers 422. Requires the `fastapi` extra when called. See
|
|
24
|
+
[Paginate a list endpoint](docs/en/how-to/paginate-a-list-endpoint.md).
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- A bare mutation of a cached model that was flushed before the enhanced `commit()` -- inside
|
|
29
|
+
a savepoint (`begin_nested()` + `flush()`), by a manual `session.flush()`, or by an
|
|
30
|
+
autoflush -- was never invalidated: the cache kept serving the pre-commit row after the
|
|
31
|
+
commit. Pending invalidations were collected by scanning `new` / `dirty` / `deleted`
|
|
32
|
+
right before the commit, and a flushed object is no longer in those sets. Every flush
|
|
33
|
+
now registers the cached instances it writes (`after_flush`); an outermost rollback
|
|
34
|
+
drops them, a savepoint rollback keeps them (at most one extra invalidation). Also
|
|
35
|
+
present in 0.5.0.
|
|
36
|
+
- A table class inheriting a field declared with a type alias and a right-hand
|
|
37
|
+
`Field(...)` (`language: Str64 = Field(index=True)` in a non-table base) lost that
|
|
38
|
+
`Field`'s attributes: the column had no index / unique constraint / primary key /
|
|
39
|
+
foreign key / `nullable` / `sa_type` / `sa_column_kwargs` / `ondelete`, and the
|
|
40
|
+
Pydantic field could lose attributes set only there (e.g. `title`). The metaclass
|
|
41
|
+
rebuilt the inherited field from the alias's `Field` alone. The inherited field is now
|
|
42
|
+
the base class's resolved field (`Base.model_fields[name]`, in which Pydantic has
|
|
43
|
+
already merged the alias's `Field` and the right-hand one): its Pydantic attributes
|
|
44
|
+
(`default`, `alias`, `validation_alias`, `title`, `description`, ...) are taken as-is,
|
|
45
|
+
`None` included, so a base `Field(alias=None)` still clears the alias's `alias` in the
|
|
46
|
+
table class. Only the SQLModel column attributes are completed: the alias's and the
|
|
47
|
+
right-hand `Field`'s column settings are folded into one (the right-hand side wins on
|
|
48
|
+
conflicts; `False` never switches off `True`), and constraints of an alias nested in
|
|
49
|
+
a union (`Alias | None = Field(...)`), which Pydantic does not merge at field level,
|
|
50
|
+
are added. Plain-typed inherited fields (`int = Field(index=True)`) were not affected.
|
|
51
|
+
Also present in 0.5.0.
|
|
52
|
+
- A cross-field validation failure of `TableViewRequest` (and the other query DTOs) used
|
|
53
|
+
as a FastAPI dependency (`after_id` with a non-zero `offset`, `after_id` with a mutable
|
|
54
|
+
`order`, an inverted time range) was a 500. Declared through `query_dependency()` it is
|
|
55
|
+
a 422 located at the offending query parameter; the documented `ValidationError`
|
|
56
|
+
exception-handler workaround is no longer needed.
|
|
57
|
+
- Every commit of a cached model through the enhanced `AsyncSession` logged a spurious
|
|
58
|
+
`WARNING` "fallback compensation triggered: ..." and invalidated the cache a second
|
|
59
|
+
time. The `after_commit` event now hands the committed pendings over to the enhanced
|
|
60
|
+
`commit()`, which invalidates each of them once. Commits that bypass the enhanced
|
|
61
|
+
`commit()` (a plain SQLAlchemy / SQLModel session, `run_sync(lambda s: s.commit())`)
|
|
62
|
+
keep the fire-and-forget compensation and its `WARNING`; it now also takes over when
|
|
63
|
+
the enhanced `commit()` fails or is cancelled after the database committed.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- A table class field declared with a type alias **and** a right-hand `Field(...)`
|
|
68
|
+
(`name: Alias = Field(...)`) now gets its Pydantic attributes exactly as Pydantic
|
|
69
|
+
resolves that declaration -- the same field plain SQLModel builds, and the same field
|
|
70
|
+
a table class gets when it inherits the declaration from a base. The right-hand
|
|
71
|
+
`Field` overrides the alias's Pydantic attributes, `None` included; sqlmodel's
|
|
72
|
+
`Field()` passes every one of them explicitly, so with a right-hand `Field` the
|
|
73
|
+
alias's `alias` / `validation_alias` / `serialization_alias` / `title` /
|
|
74
|
+
`description` no longer apply (`Field(alias=None)` clears the alias's `alias`;
|
|
75
|
+
previously the metaclass skipped `None` and kept the alias's values). The alias's
|
|
76
|
+
`default` still applies unless the right-hand `Field` sets one -- except for an alias
|
|
77
|
+
nested in a union (`Alias | None = Field(...)`), whose `Field` Pydantic does not
|
|
78
|
+
merge: such a field is required unless the right-hand `Field` sets a default. Of the
|
|
79
|
+
bundled aliases only the `Optional*Decimal*` ones carry a Pydantic attribute
|
|
80
|
+
(`default=None`), so `OptionalNonNegativeDecimal38_18 | None = Field(index=True)` is
|
|
81
|
+
now required; drop the redundant `| None` or add `default=None`. Column attributes are
|
|
82
|
+
unchanged: the alias's and the right-hand `Field`'s column settings are still folded
|
|
83
|
+
together.
|
|
84
|
+
- The cross-field rules of `PaginationRequest` and `TimeFilterRequest` are field validators:
|
|
85
|
+
a violation is still a `ValidationError` with the same message, but located at the
|
|
86
|
+
field the rule constrains (`after_id`, `created_before_datetime`,
|
|
87
|
+
`updated_before_datetime`) instead of the model root, and independent violations are
|
|
88
|
+
reported together. `TimeFilterRequest` no longer overrides `model_post_init`.
|
|
89
|
+
- `PolymorphicBaseMixin._is_joined_table_inheritance()` is renamed
|
|
90
|
+
`is_joined_table_inheritance()` (it was documented and called across modules; the
|
|
91
|
+
underscore was not a real boundary). Update direct callers.
|
|
92
|
+
- `get(table_view=...)` is annotated `TableViewArgument` (`TimeFilterRequest |
|
|
93
|
+
PageWindowRequest`), matching what it already accepted at runtime (e.g. a bare
|
|
94
|
+
`PageWindowRequest`). The `join=(Model, onclause)` ON clause is annotated with the
|
|
95
|
+
new `OnClauseArgument` alias (SQLAlchemy's public `OnClauseRole`) instead of
|
|
96
|
+
SQLAlchemy's private `_OnClauseArgument`.
|
|
97
|
+
- The type-check gate of the repository is now 0 errors **and 0 warnings**
|
|
98
|
+
(`failOnWarnings: true`).
|
|
99
|
+
|
|
100
|
+
## [0.5.0]
|
|
101
|
+
|
|
102
|
+
Upgrade guide: [Migrate from 0.4.x to 0.5.0](docs/en/how-to/migrate-to-0-5.md)
|
|
103
|
+
([中文](docs/how-to/migrate-to-0-5.md)).
|
|
104
|
+
|
|
105
|
+
### Breaking
|
|
106
|
+
|
|
107
|
+
- **`all_fields_optional=True` removed; use `partial=True`.** The old keyword raises
|
|
108
|
+
`TypeError` at class creation. `partial=True` turns inherited fields into
|
|
109
|
+
`Unset | T = Unset` (`Unset | T | None` when the base field is nullable): omitted
|
|
110
|
+
fields are `Unset` and never appear in `model_dump()` (no `exclude_unset=True`
|
|
111
|
+
needed); explicit `null` is only accepted where the base field allows `None`.
|
|
112
|
+
Replace `is None` / `is not None` "was it sent" checks with `is Unset` /
|
|
113
|
+
`is not Unset`. `partial=True` cannot be combined with `table=True`.
|
|
114
|
+
- **Optimistic-lock column renamed `version` → `oplock_version`**, now `BIGINT`
|
|
115
|
+
(bounded by `JS_MAX_SAFE_INTEGER`) with `server_default 0`, and excluded from
|
|
116
|
+
`model_dump()`. Requires a database migration (see the upgrade guide). The
|
|
117
|
+
rename is **not rolling-compatible**: 0.4.x and 0.5.0 instances cannot run
|
|
118
|
+
against the same table at once, so switch all instances together. If you
|
|
119
|
+
use `CachedTableBaseMixin`, also clear the Redis cache at the switch: cache
|
|
120
|
+
keys are unchanged and a 0.4.x entry can validate under 0.5.0 with a
|
|
121
|
+
different meaning.
|
|
122
|
+
- **`oplock_version` is a reserved name**: declaring it in the class body of any
|
|
123
|
+
`SQLModelBase` subclass raises `TypeError`.
|
|
124
|
+
- **`OptimisticLockMixin` retries conflicts 3 times by default**
|
|
125
|
+
(`__optimistic_retry_default__`); `optimistic_retry_count` now defaults to `None`
|
|
126
|
+
("use the model policy"). Pass `optimistic_retry_count=0` to fail immediately.
|
|
127
|
+
- **`delete()` normalizes errors**: an optimistic-lock conflict raises
|
|
128
|
+
`OptimisticLockError` (previously a raw `StaleDataError`); deleting a row that is
|
|
129
|
+
still referenced through a foreign key raises `ResourceReferencedError`
|
|
130
|
+
(`status_code = 409`; previously a raw `IntegrityError`).
|
|
131
|
+
- **Default primary keys are UUIDv7** (`UUIDTableBaseMixin.id` and
|
|
132
|
+
`create_subclass_id_mixin()`), previously UUIDv4. Existing rows need no migration.
|
|
133
|
+
- **`TimeFilterRequest` bounds are `AwareDatetime`**: naive datetimes are rejected.
|
|
134
|
+
`PaginationRequest.offset` is now bounded by `MAX_TABLE_VIEW_OFFSET`.
|
|
135
|
+
- **`@requires_for_update` fails closed**: it raises `RuntimeError` when it cannot
|
|
136
|
+
find an `AsyncSession` in the call arguments (previously the check was skipped).
|
|
137
|
+
- **Renamed / removed internals**: `RelationPreloadMixin._ensure_relations_loaded` →
|
|
138
|
+
`ensure_relations_loaded`; `CachedTableBaseMixin._warn_raw_dml_on_cached` →
|
|
139
|
+
`register_raw_dml_write`; `CachedTableBaseMixin._refresh_via_cache` and
|
|
140
|
+
`_has_pending_invalidation` removed.
|
|
141
|
+
- **`AsyncSession.refresh()` always reads the database** (no longer routed through
|
|
142
|
+
the Redis cache).
|
|
143
|
+
- **`get(with_for_update=True)` always implies `populate_existing`** (no opt-out).
|
|
144
|
+
- **`JSON100K` / `JSONList100K` serialize as objects / arrays** in every dump mode
|
|
145
|
+
(previously a JSON string). Dict / list input is now subject to the 100K-character
|
|
146
|
+
and nesting-depth limits as well; table models are checked at construction.
|
|
147
|
+
Subclasses overriding `model_post_init` must call `super().model_post_init(context)`.
|
|
148
|
+
- **`PositiveFloat` / `NonNegativeFloat` reject `inf` and `nan`.**
|
|
149
|
+
- **Decimal aliases enforce integer digits** (previously only total digits and
|
|
150
|
+
decimal places were validated, e.g. `SignedDecimal20_10` accepted a 15-digit integer).
|
|
151
|
+
- **Dependencies**: `pydantic>=2.12` (was `>=2.0`) and `typing-extensions>=4.14.1`.
|
|
152
|
+
- **RelationLoadChecker** (experimental) reports more findings (RLC014, session
|
|
153
|
+
subclasses) and honors `# noqa: RLCxxx` on every public entry point; endpoint
|
|
154
|
+
`noqa` comments must be on the first decorator line.
|
|
155
|
+
|
|
156
|
+
### Added
|
|
157
|
+
|
|
158
|
+
- **Tri-state fields**: `Unset` (Pydantic's `MISSING` sentinel), `partial=True`,
|
|
159
|
+
`SQLModelBase.annotation_is_omissible()` / `field_is_omissible()` /
|
|
160
|
+
`submitted_fields_among()`, `sqlmodel_ext.base.optional_dto_registry`.
|
|
161
|
+
- **Wire sentinel for callers that cannot omit keys**: `SQLModelExtConfig(omitted_sentinel=True)`
|
|
162
|
+
and `OMITTED_SENTINEL` (`'__omitted__'`).
|
|
163
|
+
- `EXCLUDE_IF_NONE` field marker (the key is dropped from every dump while the value is `None`).
|
|
164
|
+
- `sqlmodel_ext.select`: typed `select()` overloads for 5–9 column projections
|
|
165
|
+
(same object as `sqlmodel.select` at runtime). For 1–4 columns the overload set is
|
|
166
|
+
exactly upstream's, so bare attributes (`select(User.id, User.name)` →
|
|
167
|
+
`Select[tuple[UUID, str]]`) type-check; bare attributes also work for 5–9 columns,
|
|
168
|
+
and only a 5+ projection that mixes in a SQL function expression (`func.count()`)
|
|
169
|
+
should wrap every column in `col()` to keep the precise type.
|
|
170
|
+
- **Experimental `check_derived`**: `python -m sqlmodel_ext.check_derived <package>`
|
|
171
|
+
(console script `sqlmodel-ext-check-derived`) expands `partial=True` DTOs and their
|
|
172
|
+
inherited methods in a throwaway copy and runs basedpyright there, reporting only the
|
|
173
|
+
errors the expansion introduced. See
|
|
174
|
+
[Check partial DTOs for misuse](docs/en/how-to/check-partial-dtos.md).
|
|
175
|
+
- **AI rule pack** in [`ai-rules/`](ai-rules/) (`AGENTS.md` plus a `CLAUDE.md` entry
|
|
176
|
+
point) so AI coding assistants use the library the intended way. See
|
|
177
|
+
[Use with AI coding assistants](docs/en/how-to/use-with-ai-assistants.md).
|
|
178
|
+
- **`py.typed`**: the package is now PEP 561 typed, so type checkers read its inline
|
|
179
|
+
annotations.
|
|
180
|
+
- `pyrightconfig.json` and a basedpyright CI gate (the job fails on errors and when no
|
|
181
|
+
source files are scanned). See
|
|
182
|
+
[Type-check with basedpyright](docs/en/how-to/type-check-with-basedpyright.md).
|
|
183
|
+
- Runnable [`examples/`](examples/) for the new features, executed by
|
|
184
|
+
`tests/test_examples.py`.
|
|
185
|
+
- `AsyncSession`: `commit_count`, `add_post_commit_callback()`, `set_local_timeouts()`,
|
|
186
|
+
`enter_repeatable_read()`, `commit(fail_soft_when_observed=...)`,
|
|
187
|
+
`rollback(best_effort_budget_seconds=...)`, a `begin()` that routes through the
|
|
188
|
+
enhanced commit / rollback, raw-DML registration on `execute` / `exec` / `scalar` /
|
|
189
|
+
`stream` / `stream_scalars`.
|
|
190
|
+
- `sqlmodel_ext.session.SessionFactory.run_in_repeatable_read()` (whole-session retry on
|
|
191
|
+
SQLSTATE 40001), `RepeatableReadSnapshotConflictError`, `SerializationRetryExhaustedError`.
|
|
192
|
+
- CRUD: `get(skip_locked=..., authoritative=...)`, `get_one(authoritative=...)`,
|
|
193
|
+
`get_exist_one(with_for_update=...)`, `count(distinct_column=...)`, `distinct_column()`,
|
|
194
|
+
`group_sum()` / `GroupSumRow`.
|
|
195
|
+
- Keyset pagination: `PaginationRequest.after_id`, `order="id"`, `PageWindowRequest`,
|
|
196
|
+
`KeysetCursorError` / `KeysetCursorInvalidError` / `KeysetCursorUnsupportedError`.
|
|
197
|
+
- `ResourceReferencedError`, `FK_DELETE_RESTRICT_FALLBACK_MESSAGE`,
|
|
198
|
+
`TableBaseMixin.register_fk_delete_restrict_message()`.
|
|
199
|
+
- `sqlmodel_ext.mixins.uuid7` (standard-library `uuid.uuid7` on Python 3.14+).
|
|
200
|
+
- Transaction contracts: `requires_locked_param`, `requires_read_committed`,
|
|
201
|
+
`requires_repeatable_read`, `validate_locked_instances`, `SESSION_REPEATABLE_READ_KEY`.
|
|
202
|
+
- Relation preloading: `ensure_relations_loaded_bulk()`, `bulk_preload_unsupported_reason()`.
|
|
203
|
+
- Cache: `CachedTableBaseMixin.invalidate_on_commit()`, `invalidate_all(strict=...)`,
|
|
204
|
+
`register_raw_dml_write()`; `CACHE_TTL_HOT` / `CACHE_TTL_WARM` / `CACHE_TTL_COLD`;
|
|
205
|
+
`collect_migration_invalidation_tasks()` / `run_pending_migration_cache_invalidations()`
|
|
206
|
+
(new optional extra `alembic`).
|
|
207
|
+
- New mixins: `ResourceQuotaMixin` (with `QuotaExceededError`, `QuotaOwnerNotFoundError`,
|
|
208
|
+
`CallerDidNotCommitError`), `TrgmSearchableMixin` / `TrgmSearchRequest` /
|
|
209
|
+
`BIGRAM_FUNCTION_SQL`, `MixinTableScanMixin`.
|
|
210
|
+
- Field types: `max_length_of()`, `Str1`, `Text3072`, `Text4K`, `NonEmptyStrippedStr32`,
|
|
211
|
+
`SingleLineStr64`, `SearchQueryStr64`, `HttpHeaderName`, `SignedBigInt`, `INT32_MIN`,
|
|
212
|
+
`ClientIPAddress`, `OptionalSignedDecimal38_18`, `NullableNonNegativeDecimal20_10`,
|
|
213
|
+
write/sum Decimal pairs (`SignedWriteDecimal38_18`, `NonNegativeWriteDecimal38_18`,
|
|
214
|
+
`PositiveWriteDecimal38_18`, `OptionalNonNegativeWriteDecimal38_18`,
|
|
215
|
+
`OptionalSignedWriteDecimal38_18`, `SignedSumDecimal38_18`) and
|
|
216
|
+
`DECIMAL_38_18_COLUMN_DIGITS` / `DECIMAL_38_18_WRITE_DIGITS` / `DECIMAL_38_18_PLACES`;
|
|
217
|
+
`ensure_json_within_limits()` in `sqlmodel_ext.field_types.dialects.postgresql`.
|
|
218
|
+
- RelationLoadChecker: RLC014 and module-level `conditional_commit_methods` /
|
|
219
|
+
`explicit_commit_methods` / `dependency_commit_methods`.
|
|
220
|
+
|
|
221
|
+
### Fixed
|
|
222
|
+
|
|
223
|
+
- A raw `insert(...)` into a cached table executed through the enhanced
|
|
224
|
+
`AsyncSession` left cached query results stale after commit (until TTL). It
|
|
225
|
+
now registers a query-level invalidation that the enhanced `commit()` runs.
|
|
226
|
+
`text()` writes and writes nested in a `SELECT` (writable CTEs) are still not
|
|
227
|
+
invalidated after commit -- register `invalidate_on_commit` or call
|
|
228
|
+
`invalidate_all` yourself.
|
|
229
|
+
- An explicit `default=None` inside merged `Field` metadata was dropped, silently making
|
|
230
|
+
the field required.
|
|
231
|
+
- On sqlmodel ≥ 0.0.32, `id: NonNegativeInt = Field(primary_key=True)` lost its primary key.
|
|
232
|
+
- Cached-model serialization dropped `exclude=True` columns.
|
|
233
|
+
- The cache could serve stale reads or publish data that was not yet committed.
|
|
234
|
+
- Read-modify-write after `with_for_update=True` could lose an update through a stale
|
|
235
|
+
identity-map object.
|
|
236
|
+
- A joined-table-inheritance update that touched only child-table columns did not
|
|
237
|
+
advance `updated_at`.
|
|
238
|
+
- `ExtraIgnoreModelBase` warned about keys accepted through `AliasChoices`.
|
|
239
|
+
- `model_json_schema()` did not inject the sentinel branch into self-referencing models.
|
|
240
|
+
- `AsyncSession.execute()` / `exec()` / `scalar()` / `stream()` / `stream_scalars()`
|
|
241
|
+
no longer degrade their return type to `Any`: they keep upstream SQLModel's
|
|
242
|
+
signatures (`await session.exec(select(User))` is `ScalarResult[User]`).
|
|
243
|
+
- Optimistic-lock models no longer emit Pydantic's "Field name ... shadows an
|
|
244
|
+
attribute in parent" `UserWarning` for the version column.
|
|
245
|
+
|
|
246
|
+
### Changed
|
|
247
|
+
|
|
248
|
+
- Documentation rewritten around the single-source-of-truth design, with new pages for
|
|
249
|
+
`Unset`, PATCH endpoints, basedpyright and this migration.
|
|
250
|
+
|
|
251
|
+
### Known limitations
|
|
252
|
+
|
|
253
|
+
- A Redis failure during post-commit cache invalidation is logged, not raised (the
|
|
254
|
+
database has already committed): the affected cache entries keep serving
|
|
255
|
+
pre-commit data until their TTL expires. Use `no_cache=True` for reads that must
|
|
256
|
+
not rely on the cache.
|
|
257
|
+
- On Python 3.12, `JSON100K | None` as a `table=True` field raises
|
|
258
|
+
`has no matching SQLAlchemy type`; declare the column explicitly with
|
|
259
|
+
`Field(default=None, sa_type=JSONB)` (pre-existing).
|
|
260
|
+
|
|
261
|
+
## 0.4.2 and earlier
|
|
262
|
+
|
|
263
|
+
Not recorded in this file; see the git history (tags `v0.4.0` and earlier).
|