sqlmodel-ext 0.5.1__tar.gz → 0.5.2__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.5.1 → sqlmodel_ext-0.5.2}/CHANGELOG.md +51 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/PKG-INFO +1 -1
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/metaclass.md +4 -4
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/metaclass.md +4 -4
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/pyproject.toml +1 -1
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/__init__.py +1 -1
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/base.py +123 -134
- sqlmodel_ext-0.5.2/tests/test_metaclass_field_resolution_052.py +406 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/.github/workflows/ci.yml +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/.gitignore +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/LICENSE +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/README.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/README_zh.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/ai-rules/AGENTS.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/ai-rules/CLAUDE.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/ai-rules/README.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/.vitepress/config.mts +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/.vitepress/en.mts +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/.vitepress/theme/index.ts +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/.vitepress/theme/style/custom.css +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/.vitepress/zh.mts +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/cached-table.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/cascade-delete-semantics.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/crud-pipeline.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/optimistic-lock.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/polymorphic-internals.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/prerequisites.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/relation-load-checker.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/relation-preload.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/single-source-of-truth.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/transactional-cache-transparency.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/explanation/unset-three-state.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/guide/cached-table.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/guide/crud.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/guide/field-types.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/guide/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/guide/optimistic-lock.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/guide/pagination.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/guide/polymorphic.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/guide/relation-load-checker.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/guide/relation-preload.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/aggregate-queries.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/cache-queries.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/check-partial-dtos.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/configure-cascade-delete.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/define-jti-models.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/define-sti-models.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/enforce-locking-and-isolation.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/extra-mixins.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/handle-concurrent-updates.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/handle-referenced-deletes.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/integrate-with-fastapi.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/keyset-pagination.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/migrate-to-0-5.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/paginate-a-list-endpoint.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/prevent-missing-greenlet.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/release-connection-during-long-io.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/type-check-with-basedpyright.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/use-with-ai-assistants.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/how-to/write-patch-endpoints.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/internals/cached-table.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/internals/crud.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/internals/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/internals/metaclass.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/internals/optimistic-lock.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/internals/polymorphic.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/internals/prerequisites.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/internals/relation-load-checker.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/internals/relation-preload.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/reference/base-classes.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/reference/crud-methods.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/reference/decorators.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/reference/field-types.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/reference/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/reference/mixins.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/reference/pagination-types.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/tutorials/01-getting-started.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/tutorials/02-building-a-blog-api.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/tutorials/03-adding-redis-cache.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/en/tutorials/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/cached-table.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/cascade-delete-semantics.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/crud-pipeline.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/optimistic-lock.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/polymorphic-internals.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/prerequisites.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/relation-load-checker.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/relation-preload.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/single-source-of-truth.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/transactional-cache-transparency.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/explanation/unset-three-state.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/guide/cached-table.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/guide/crud.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/guide/field-types.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/guide/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/guide/optimistic-lock.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/guide/pagination.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/guide/polymorphic.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/guide/relation-load-checker.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/guide/relation-preload.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/aggregate-queries.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/cache-queries.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/check-partial-dtos.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/configure-cascade-delete.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/define-jti-models.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/define-sti-models.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/enforce-locking-and-isolation.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/extra-mixins.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/handle-concurrent-updates.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/handle-referenced-deletes.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/integrate-with-fastapi.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/keyset-pagination.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/migrate-to-0-5.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/paginate-a-list-endpoint.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/prevent-missing-greenlet.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/release-connection-during-long-io.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/type-check-with-basedpyright.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/use-with-ai-assistants.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/how-to/write-patch-endpoints.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/internals/cached-table.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/internals/crud.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/internals/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/internals/metaclass.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/internals/optimistic-lock.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/internals/polymorphic.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/internals/prerequisites.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/internals/relation-load-checker.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/internals/relation-preload.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/reference/base-classes.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/reference/crud-methods.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/reference/decorators.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/reference/field-types.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/reference/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/reference/mixins.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/reference/pagination-types.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/tutorials/01-getting-started.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/tutorials/02-building-a-blog-api.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/tutorials/03-adding-redis-cache.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/docs/tutorials/index.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/01_unset_patch.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/02_single_source_types.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/03_keyset_pagination.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/04_locking_and_isolation.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/05_aggregates.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/06_referenced_delete.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/07_transactional_cache.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/08_optimistic_lock.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/09_relation_preload_bulk.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/10_typed_select.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/11_type_errors_caught_by_basedpyright.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/README.md +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/check_derived_demo/pyrightconfig.json +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/check_derived_demo/shop/__init__.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/check_derived_demo/shop/handlers.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/check_derived_demo/shop/models.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/examples/consumer_integration/run.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/package.json +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/pnpm-lock.yaml +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/pyrightconfig.json +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/_compat.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/_exceptions.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/_sa_type.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/_type_unwrap.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/_utils.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/check_derived.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/constants.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/dependencies.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/derived_decls.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/__init__.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/_internal/__init__.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/_internal/path.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/_ssrf.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/dialects/__init__.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/dialects/postgresql/__init__.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/dialects/postgresql/array.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/dialects/postgresql/exceptions.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/dialects/postgresql/jsonb_types.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/dialects/postgresql/numpy_vector.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/ip_address.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/mixins/__init__.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/mixins/module_name_mixin.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/field_types/url.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/__init__.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/_uuid.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/cached_table.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/constants.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/exceptions.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/info_response.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/migration_cache_invalidation.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/mixin_table_scan.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/optimistic_lock.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/polymorphic.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/relation_preload.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/resource_quota.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/table.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/mixins/trgm_searchable.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/pagination.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/py.typed +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/relation_load_checker.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/select.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/session.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/src/sqlmodel_ext/unset.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/__init__.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/_models.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/conftest.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_annotated_default_factory_inheritance.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_array_explicit_field_sa_type.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_basic_crud.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_cached_commit_paths.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_cached_table.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_check_derived.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_crud_extended.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_decimal_types.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_deferred_index.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_derived_decls.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_enhanced_session.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_examples.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_field_types_aliases_extra.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_field_types_jsonb_limits.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_field_types_misc.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_field_types_str_num.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_field_types_url_ssrf.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_051_rereview.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_051_review.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_051_review2.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_check_derived_review2.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_check_derived_temp_root.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_nested_duplicate_names.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_omitted_sentinel_alias_modes.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_partial_alias_generator.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_partial_annotated_field_warnings.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_partial_discriminator.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_partial_frozen_field.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_partial_rhs_field_metadata.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_review4_followup.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_final_review5.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_inherited_column_attributes.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_metaclass_extra.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_mixins_extra.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_optimistic_lock.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_pagination.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_partial.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_pg_dialect_types.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_polymorphic_jti.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_polymorphic_sti_query.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_query_dependency.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_query_dependency_metadata.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_relation_load_checker.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_relation_load_checker_load_resolution.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_relation_load_checker_sessions.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_relation_preload.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_relationship_pep604.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_review_mixed_version_cache.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_review_raw_insert_cache.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_review_raw_insert_edges.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_review_raw_insert_late_model.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_review_raw_insert_sti.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_select.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_smoke_imports.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_sti_default_isolation.py +0 -0
- {sqlmodel_ext-0.5.1 → sqlmodel_ext-0.5.2}/tests/test_unset.py +0 -0
|
@@ -7,6 +7,57 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
7
7
|
> there is no stability or backward-compatibility guarantee between versions, and
|
|
8
8
|
> you use it at your own risk. Pin the exact version you depend on.
|
|
9
9
|
|
|
10
|
+
## [0.5.2]
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- A table class field declared with a type alias and a plain right-hand value
|
|
15
|
+
(`x: NonNegativeDecimal38_18 = Decimal(0)`, `s: Str64 = 'a'`) had its default only in
|
|
16
|
+
`model_fields`: the JSON schema listed the field as `required` with no `default`, and
|
|
17
|
+
`model_validate({})` rejected the payload with "Field required". With an alias that
|
|
18
|
+
carries its own default (`opt: OptionalNonNegativeDecimal38_18 = Decimal(1)`),
|
|
19
|
+
`model_fields` even showed the alias's default (`None`) instead of the declared one.
|
|
20
|
+
The metaclass set the value on the field after constructing it, so Pydantic did not
|
|
21
|
+
count it as set when it built the core schema. A metaclass rebuild (an inherited field
|
|
22
|
+
description, a relationship) hid the defect in some classes. The same applied to a
|
|
23
|
+
field re-declared in the table class body without a right-hand side, which takes the
|
|
24
|
+
base's default. The table class now matches a non-table class with the same
|
|
25
|
+
declaration (schema, validation and `model_fields`). Non-table classes and fields the
|
|
26
|
+
table class inherits without re-declaring were not affected. Also present in 0.5.0
|
|
27
|
+
and 0.5.1.
|
|
28
|
+
- **Behavior change.** A table class field declared with **several** `Field(...)` in one
|
|
29
|
+
`Annotated` and no right-hand side (`value: Legacy` with
|
|
30
|
+
`Legacy = Annotated[str, Field(alias='legacy', title='T'), Field(unique=True)]`, or a
|
|
31
|
+
nested alias such as `Annotated[Str64, Field(unique=True)]`) now gets its Pydantic
|
|
32
|
+
attributes as Pydantic merges them -- the same field a non-table class, plain
|
|
33
|
+
SQLModel and a table class inheriting the declaration already got. A later `Field`
|
|
34
|
+
overrides an earlier one, `None` included, and sqlmodel's `Field()` passes
|
|
35
|
+
`alias` / `validation_alias` / `serialization_alias` / `title` / `description` /
|
|
36
|
+
`default_factory` / `discriminator` / `exclude` explicitly as `None` and `repr` as
|
|
37
|
+
`True`. So when an earlier `Field` of the annotation sets any of these and a later
|
|
38
|
+
one does not, the table class no longer keeps the earlier value: the alias / title /
|
|
39
|
+
description are gone, and a `default_factory` is dropped, which makes the field
|
|
40
|
+
**required**. A `default` is not affected (sqlmodel's `Field()` does not pass it).
|
|
41
|
+
None of the bundled aliases combines several `Field`s or sets such an attribute. To keep the value, set it in the last `Field` or on a right-hand
|
|
42
|
+
`= Field(...)`. The same holds for a non-table class whose field needs an injected
|
|
43
|
+
`sa_type` (`Annotated[FilePathType, Field(alias='p'), Field(index=True)]`), which kept
|
|
44
|
+
the earlier `alias` too. The same path also applied the `Field` of an alias nested in a
|
|
45
|
+
union with no right-hand side, which Pydantic does not merge at field level:
|
|
46
|
+
`x: OptionalNonNegativeDecimal38_18 | None` declared in a table class had
|
|
47
|
+
`default=None`; it is now **required**, as it already was in a non-table class, when
|
|
48
|
+
inherited, and with a right-hand `Field` that sets no default (0.5.1). Drop the
|
|
49
|
+
redundant `| None` or write `= None`. The column attributes of all the declaration's `Field`s are
|
|
50
|
+
still folded into the column, now with the precedence Pydantic uses for the Pydantic
|
|
51
|
+
attributes: the right-hand `Field` first, then the annotation's `Field`s from the last
|
|
52
|
+
to the first; `False` still never switches off `True`. So a table class **inheriting**
|
|
53
|
+
such a declaration agrees with one declaring it directly:
|
|
54
|
+
`Annotated[NonNegativeDecimal38_18, Field(sa_type=Numeric(20, 2))]` gives
|
|
55
|
+
`NUMERIC(20, 2)` in both (inheriting it gave `NUMERIC(38, 18)`, the alias's
|
|
56
|
+
`sa_type`). Plain SQLModel, which reads only the first carrier, still gives
|
|
57
|
+
`NUMERIC(38, 18)`. Also present in 0.5.0 and 0.5.1. This only changes the metadata
|
|
58
|
+
the models build: existing databases are not altered. Tables created from now on
|
|
59
|
+
(`create_all`) get the new column type; to align an existing table, write a migration.
|
|
60
|
+
|
|
10
61
|
## [0.5.1]
|
|
11
62
|
|
|
12
63
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sqlmodel-ext
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: Extended SQLModel infrastructure: smart metaclass, async CRUD mixins, polymorphic inheritance, optimistic locking, relation preloading, and reusable field types.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Foxerine/sqlmodel-ext
|
|
6
6
|
Project-URL: Repository, https://github.com/Foxerine/sqlmodel-ext
|
|
@@ -142,7 +142,7 @@ This snapshot must be taken **before** any annotation injection: later steps inj
|
|
|
142
142
|
|
|
143
143
|
### Step 4.5: Recover SQLModel attributes from `Annotated[T, Field(...)]`
|
|
144
144
|
|
|
145
|
-
When Pydantic v2 processes `Annotated` metadata it replaces `sqlmodel.main.FieldInfo` with `pydantic.fields.FieldInfo`, which knows nothing about SQLModel attributes such as `foreign_key` or `sa_type`. For **table classes** (including `Annotated` fields inherited from parents), `_recover_annotated_sqlmodel_fields()` converts them back into the `= Field(...)` form; non-table classes are left as-is so child table classes can inherit them.
|
|
145
|
+
When Pydantic v2 processes `Annotated` metadata it replaces `sqlmodel.main.FieldInfo` with `pydantic.fields.FieldInfo`, which knows nothing about SQLModel attributes such as `foreign_key` or `sa_type`. For **table classes** (including `Annotated` fields inherited from parents), `_recover_annotated_sqlmodel_fields()` converts them back into the `= Field(...)` form; non-table classes are left as-is so child table classes can inherit them. Every such field is rebuilt from **Pydantic's own resolution of its declaration** — the field a non-table class (or plain SQLModel) gets for the same declaration: `FieldInfo.from_annotated_attribute()` when the table class declares it with a right-hand side (`= Field(...)` or a plain value such as `= 'a'`), `FieldInfo.from_annotation()` when it declares it without one, and the base's resolved field `Base.model_fields[name]` when the table class **inherits** it (without re-declaring it). Its Pydantic attributes are taken as-is. Several `Field`s in one `Annotated` merge as Pydantic merges them: a later `Field` overrides an earlier one, `None` included — sqlmodel's `Field()` passes `alias` / `validation_alias` / `serialization_alias` / `title` / `description` / `default_factory` / `discriminator` / `exclude` explicitly as `None` (and `repr` as `True`), so `Annotated[str, Field(alias='a', title='T'), Field(unique=True)]` has neither alias nor title; a right-hand `Field` overrides them all. The `Field` of an alias nested in a union (`OptionalNonNegativeDecimal38_18 | None`) is not merged at field level, so its `default=None` does not apply — only its constraints and column attributes are carried over. The attributes are passed to the constructor, so Pydantic records them as set and they reach the core schema: a default of `x: Str64 = 'a'` is in the JSON schema and in validation, not only in `model_fields`. Only the column attributes are completed — the `FieldInfoMetadata` carriers of all the declaration's `Field`s are folded into one, since SQLModel reads only the first: the right-hand `Field`'s first, then the annotation's from the last to the first (`Annotated[NonNegativeDecimal38_18, Field(sa_type=Numeric(20, 2))]` gets `NUMERIC(20, 2)`), and `False` never switches off `True`. One library rule departs from Pydantic: a field re-declared in the table class body without a right-hand side keeps the base's default (Pydantic would make it required). Metadata that stays in the class annotation (an `AfterValidator` next to the alias's `Field`, for example) is not copied into the field, so it is applied once.
|
|
146
146
|
|
|
147
147
|
### Step 4.5.b: `oplock_version` is a reserved name
|
|
148
148
|
|
|
@@ -182,8 +182,8 @@ for field_name, field_type in annotations.items():
|
|
|
182
182
|
if sa_type is not None:
|
|
183
183
|
field_value = attrs.get(field_name, Undefined)
|
|
184
184
|
if field_value is Undefined:
|
|
185
|
-
# no "= Field(...)":
|
|
186
|
-
#
|
|
185
|
+
# no "= Field(...)": a right-hand Field carrying Pydantic's own resolution of the
|
|
186
|
+
# Annotated (default_factory etc. kept, nothing changed) plus sa_type
|
|
187
187
|
annotated_fi = _find_field_info_in_annotated(field_type)
|
|
188
188
|
attrs[field_name] = annotated_fi if annotated_fi is not None else Field(sa_type=sa_type)
|
|
189
189
|
elif isinstance(field_value, FieldInfo):
|
|
@@ -218,7 +218,7 @@ result = super().__new__(cls, name, bases, attrs, **kwargs)
|
|
|
218
218
|
# merge the saved SQLModelFieldInfo back and rebuild the Column
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
Step 7 only copies back what Pydantic filled in; there `None` and `False` never overwrite a saved value. The `FieldInfoMetadata` carriers of one declaration were already folded into one in step 4.5 (SQLModel reads only the first; otherwise an all-unset carrier inside a type alias shadows `= Field(primary_key=True)` — which is exactly why `id: NonNegativeInt = Field(primary_key=True)` lost its primary key on sqlmodel ≥ 0.0.32).
|
|
222
222
|
|
|
223
223
|
### Steps 8–9: Relationship fields under inheritance
|
|
224
224
|
|
|
@@ -142,7 +142,7 @@ _own_annotation_names = frozenset(annotations)
|
|
|
142
142
|
|
|
143
143
|
### 第 4.5 步:恢复 `Annotated[T, Field(...)]` 中的 SQLModel 属性
|
|
144
144
|
|
|
145
|
-
Pydantic v2 处理 `Annotated` 元数据时会把 `sqlmodel.main.FieldInfo` 换成 `pydantic.fields.FieldInfo`,后者不认识 `foreign_key`、`sa_type` 等 SQLModel 属性。`_recover_annotated_sqlmodel_fields()` 对 **table 类**(包括从父类继承来的 `Annotated` 字段)把它们还原成 `= Field(...)` 形式;非 table 类保留原样,供子 table
|
|
145
|
+
Pydantic v2 处理 `Annotated` 元数据时会把 `sqlmodel.main.FieldInfo` 换成 `pydantic.fields.FieldInfo`,后者不认识 `foreign_key`、`sa_type` 等 SQLModel 属性。`_recover_annotated_sqlmodel_fields()` 对 **table 类**(包括从父类继承来的 `Annotated` 字段)把它们还原成 `= Field(...)` 形式;非 table 类保留原样,供子 table 类继承。每个这样的字段都以 **Pydantic 对该声明自己的解析结果**为准重建——也就是同一份声明写在非 table 类(或纯 SQLModel)里得到的字段:table 类自己声明且带右侧值(`= Field(...)` 或 `= 'a'` 这样的普通值)时用 `FieldInfo.from_annotated_attribute()`,自己声明但没有右侧值时用 `FieldInfo.from_annotation()`,table 类**继承**(未在类体里重新声明)时用基类已解析的字段 `Base.model_fields[name]`。Pydantic 层属性原样采用。同一个 `Annotated` 里的多个 `Field` 按 Pydantic 的规则合并:后一个 `Field` 覆盖前一个,`None` 也算——sqlmodel 的 `Field()` 会把 `alias` / `validation_alias` / `serialization_alias` / `title` / `description` / `default_factory` / `discriminator` / `exclude` 显式传成 `None`(`repr` 传成 `True`),所以 `Annotated[str, Field(alias='a', title='T'), Field(unique=True)]` 既没有 alias 也没有 title;右侧 `Field` 覆盖它们全部。嵌在联合类型里的别名(`OptionalNonNegativeDecimal38_18 | None`)的 `Field` 不在字段层合并,所以它的 `default=None` 不生效——只带过来它的约束与列属性。这些属性经构造函数传入,Pydantic 把它们记为"已设置",于是能进入 core schema:`x: Str64 = 'a'` 的默认值会出现在 JSON schema 与校验里,而不只是在 `model_fields` 里。只补全列属性——声明中所有 `Field` 的 `FieldInfoMetadata` 载体被折叠成一个,因为 SQLModel 只读第一个:先右侧 `Field` 的,再按注解里从后往前的顺序(`Annotated[NonNegativeDecimal38_18, Field(sa_type=Numeric(20, 2))]` 得到 `NUMERIC(20, 2)`),且 `False` 不会关掉 `True`。有一条本库规则与 Pydantic 不同:在 table 类体里重新声明、但没有右侧值的字段,沿用基类的默认值(Pydantic 会让它变成必填)。仍留在类注解里的元数据(例如与别名 `Field` 并列的 `AfterValidator`)不会再复制进字段,因此只执行一次。
|
|
146
146
|
|
|
147
147
|
### 第 4.5.b 步:`oplock_version` 是保留名
|
|
148
148
|
|
|
@@ -182,8 +182,8 @@ for field_name, field_type in annotations.items():
|
|
|
182
182
|
if sa_type is not None:
|
|
183
183
|
field_value = attrs.get(field_name, Undefined)
|
|
184
184
|
if field_value is Undefined:
|
|
185
|
-
# 没有 "= Field(...)"
|
|
186
|
-
#
|
|
185
|
+
# 没有 "= Field(...)":造一个携带 Pydantic 对该 Annotated 自身解析结果的右侧 Field
|
|
186
|
+
# (default_factory 等原样保住,不改变任何东西),再把 sa_type 加进去
|
|
187
187
|
annotated_fi = _find_field_info_in_annotated(field_type)
|
|
188
188
|
attrs[field_name] = annotated_fi if annotated_fi is not None else Field(sa_type=sa_type)
|
|
189
189
|
elif isinstance(field_value, FieldInfo):
|
|
@@ -218,7 +218,7 @@ result = super().__new__(cls, name, bases, attrs, **kwargs)
|
|
|
218
218
|
# 用保存的 SQLModelFieldInfo 合并回去并重建 Column
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
第 7 步只把 Pydantic 填上的值拷回来,此处 `None` 与 `False` 不会覆盖已保存的值。同一声明的 `FieldInfoMetadata` 载体在第 4.5 步已折叠成一个(SQLModel 只读第一个;否则类型别名里全空的载体会遮住 `= Field(primary_key=True)`——这正是 sqlmodel ≥ 0.0.32 上 `id: NonNegativeInt = Field(primary_key=True)` 丢失主键的原因)。
|
|
222
222
|
|
|
223
223
|
### 第 8–9 步:继承中的关系字段
|
|
224
224
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sqlmodel-ext"
|
|
7
|
-
version = "0.5.
|
|
7
|
+
version = "0.5.2"
|
|
8
8
|
description = "Extended SQLModel infrastructure: smart metaclass, async CRUD mixins, polymorphic inheritance, optimistic locking, relation preloading, and reusable field types."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -20,7 +20,7 @@ Quick start::
|
|
|
20
20
|
user = await user.save(session)
|
|
21
21
|
users = await User.get(session, fetch_mode="all")
|
|
22
22
|
"""
|
|
23
|
-
__version__ = "0.5.
|
|
23
|
+
__version__ = "0.5.2"
|
|
24
24
|
|
|
25
25
|
# Every re-export below uses the redundant ``X as X`` form: the package ships
|
|
26
26
|
# ``py.typed``, and type checkers treat a plain ``from m import X`` in a typed
|
|
@@ -129,11 +129,13 @@ def _merge_field_info_attrs(target: SQLModelFieldInfo, source: FieldInfo) -> Non
|
|
|
129
129
|
"""
|
|
130
130
|
Merge explicitly-set attributes from ``source`` into ``target``.
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
132
|
+
Only used by the metaclass's final restore step (step 7), where ``source``
|
|
133
|
+
is the field Pydantic built **from** ``target`` and ``target`` gets back
|
|
134
|
+
the values Pydantic filled in; there ``None`` never means "cleared", so
|
|
135
|
+
skipping it is correct. It is **not** a field-declaration merge: the
|
|
136
|
+
``Field(...)`` objects of one declaration (several ``Field`` in one
|
|
137
|
+
``Annotated``, an alias plus a right-hand ``= Field(...)``) follow
|
|
138
|
+
Pydantic's resolution, ``None`` included (see ``_inherit_resolved_field``).
|
|
137
139
|
Merges attributes dynamically (via ``__slots__`` / ``__annotations__`` / ``__dict__``),
|
|
138
140
|
no hardcoded attribute names — upstream additions are handled automatically.
|
|
139
141
|
|
|
@@ -205,10 +207,10 @@ def _merge_sqlmodel_metadata_carrier(
|
|
|
205
207
|
Since sqlmodel 0.0.32, ``Field(primary_key=..., sa_type=..., ...)`` stores the
|
|
206
208
|
SQLModel-specific attributes in a ``FieldInfoMetadata`` entry of
|
|
207
209
|
``metadata``, and SQLModel reads only the **first** such entry. Plain
|
|
208
|
-
concatenation would therefore let an earlier, all-unset carrier
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
concatenation would therefore let an earlier, all-unset carrier shadow the
|
|
211
|
+
values set by the later source. Only ``_merge_field_info_attrs`` (the
|
|
212
|
+
metaclass's restore step) uses it; the carriers of one field declaration
|
|
213
|
+
are folded by ``_fold_column_carriers``.
|
|
212
214
|
|
|
213
215
|
Set values of the source carrier are folded into a **copy** of the target
|
|
214
216
|
carrier (carriers inside type aliases are shared singletons and must not be
|
|
@@ -248,7 +250,7 @@ Public Pydantic ``FieldInfo`` attributes an inherited field takes verbatim from
|
|
|
248
250
|
|
|
249
251
|
|
|
250
252
|
def _inherit_resolved_field(
|
|
251
|
-
|
|
253
|
+
alias_fis: list[SQLModelFieldInfo],
|
|
252
254
|
base_fi: FieldInfo,
|
|
253
255
|
annotation_metadata: list[Any],
|
|
254
256
|
) -> SQLModelFieldInfo:
|
|
@@ -256,81 +258,91 @@ def _inherit_resolved_field(
|
|
|
256
258
|
Build the ``FieldInfo`` of a table-class field from Pydantic's resolution of its declaration.
|
|
257
259
|
|
|
258
260
|
``base_fi`` is the field as Pydantic resolves the declaration
|
|
259
|
-
``name: Alias =
|
|
260
|
-
|
|
261
|
-
declaration
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
``name: Alias [= value]``: the base class's entry in ``model_fields`` for
|
|
262
|
+
an inherited field; ``FieldInfo.from_annotated_attribute`` for a
|
|
263
|
+
declaration with a right-hand side (``= Field(...)`` or a plain value) in
|
|
264
|
+
the table class itself; ``FieldInfo.from_annotation`` for one without.
|
|
265
|
+
Pydantic has merged every ``Field(...)`` of the declaration into it -- the
|
|
266
|
+
ones inside the ``Annotated`` (later ones override earlier ones, ``None``
|
|
267
|
+
included) and the right-hand one (which overrides them all). ``alias_fis``
|
|
268
|
+
are the SQLModel ``Field(...)`` objects found in the annotation, in
|
|
269
|
+
declaration order. ``base_fi`` is the single source of truth for the field:
|
|
264
270
|
|
|
265
271
|
- **Pydantic attributes** (``default`` / ``default_factory`` / ``alias`` /
|
|
266
272
|
``validation_alias`` / ``serialization_alias`` / ``title`` /
|
|
267
273
|
``description`` / ``examples`` / ``exclude`` / ``frozen`` / ...) are
|
|
268
274
|
taken from ``base_fi`` verbatim, ``None`` included: ``Field(alias=None)``
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
275
|
+
clears an earlier ``alias`` and the table class must see the same field
|
|
276
|
+
as a non-table class does. They are passed to the constructor so that
|
|
277
|
+
Pydantic records every one of them as explicitly set -- the same
|
|
272
278
|
treatment Pydantic gives a resolved ``FieldInfo`` reused as a default.
|
|
279
|
+
Setting them afterwards (``fi.default = value``) would not record them:
|
|
280
|
+
Pydantic would then drop them when it builds the field and its core
|
|
281
|
+
schema, while ``model_fields`` (restored by the metaclass) keeps them.
|
|
273
282
|
- **Metadata** starts from ``base_fi.metadata`` minus ``annotation_metadata``:
|
|
274
283
|
the metadata Pydantic collects from the rebuilt class annotation (the
|
|
275
284
|
``Annotated`` arguments other than the SQLModel ``Field``, e.g. an
|
|
276
285
|
``AfterValidator``). Pydantic adds those again when it builds the
|
|
277
286
|
field, so keeping them here would run such a validator twice. Each
|
|
278
|
-
annotation item removes one equal item. Items of ``
|
|
279
|
-
whose type ``base_fi`` lacks are appended: an alias nested in a
|
|
280
|
-
(``Alias | None
|
|
281
|
-
|
|
282
|
-
absent from ``base_fi``.
|
|
287
|
+
annotation item removes one equal item. Items of the ``alias_fis``
|
|
288
|
+
metadata whose type ``base_fi`` lacks are appended: an alias nested in a
|
|
289
|
+
union (``Alias | None``) is not merged by Pydantic at field level, so its
|
|
290
|
+
constraints (``MaxLen`` -> column length) are absent from ``base_fi``.
|
|
283
291
|
- **Column attributes**: SQLModel reads only the first ``FieldInfoMetadata``
|
|
284
|
-
carrier
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
292
|
+
carrier. All carriers of the declaration are folded into one (see
|
|
293
|
+
``_fold_column_carriers``), by the precedence Pydantic applies to the
|
|
294
|
+
Pydantic attributes: the carriers of ``base_fi`` that are not the
|
|
295
|
+
annotation's (those of the right-hand ``Field``) first, then the
|
|
296
|
+
annotation's from the last ``Field`` to the first. The folded values
|
|
297
|
+
are also set as instance attributes for readers using
|
|
298
|
+
``getattr(field, 'primary_key')``.
|
|
299
|
+
|
|
300
|
+
Neither ``alias_fis`` nor their carriers are mutated.
|
|
290
301
|
"""
|
|
291
302
|
kwargs: dict[str, Any] = {name: getattr(base_fi, name) for name in _PYDANTIC_FIELD_ATTRS}
|
|
292
303
|
inherited = SQLModelFieldInfo(**kwargs)
|
|
293
304
|
|
|
305
|
+
alias_meta = [item for alias_fi in alias_fis for item in alias_fi.metadata]
|
|
306
|
+
alias_carriers = [item for item in alias_meta if isinstance(item, FieldInfoMetadata)]
|
|
307
|
+
base_types = {type(item) for item in base_fi.metadata}
|
|
294
308
|
base_meta = list(base_fi.metadata)
|
|
295
309
|
for item in annotation_metadata:
|
|
296
310
|
base_meta.remove(item)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
item
|
|
300
|
-
if isinstance(item, FieldInfoMetadata) or type(item) not in base_types
|
|
311
|
+
right_hand_carriers = [
|
|
312
|
+
item for item in base_meta
|
|
313
|
+
if isinstance(item, FieldInfoMetadata) and not any(item is c for c in alias_carriers)
|
|
301
314
|
]
|
|
302
|
-
metadata =
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
315
|
+
metadata = [item for item in base_meta if not isinstance(item, FieldInfoMetadata)]
|
|
316
|
+
metadata += [
|
|
317
|
+
item for item in alias_meta
|
|
318
|
+
if not isinstance(item, FieldInfoMetadata) and type(item) not in base_types
|
|
319
|
+
]
|
|
320
|
+
carrier = _fold_column_carriers(right_hand_carriers + alias_carriers[::-1])
|
|
306
321
|
if carrier is not None:
|
|
322
|
+
metadata.append(carrier)
|
|
307
323
|
for fim_field in dataclasses.fields(carrier):
|
|
308
324
|
value = getattr(carrier, fim_field.name)
|
|
309
325
|
if value is not _FIM_UNSET_SA_TYPE:
|
|
310
326
|
setattr(inherited, fim_field.name, value)
|
|
327
|
+
inherited.metadata = metadata
|
|
311
328
|
return inherited
|
|
312
329
|
|
|
313
330
|
|
|
314
|
-
def _fold_column_carriers(
|
|
331
|
+
def _fold_column_carriers(carriers: list[FieldInfoMetadata]) -> FieldInfoMetadata | None:
|
|
315
332
|
"""
|
|
316
|
-
Fold
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
aliases are shared); the others are dropped. Non-carrier items are kept
|
|
325
|
-
unchanged and in order.
|
|
333
|
+
Fold ``FieldInfoMetadata`` carriers, ordered by precedence, into a new one.
|
|
334
|
+
|
|
335
|
+
An earlier carrier's set value wins; a later carrier only fills attributes
|
|
336
|
+
the earlier ones left unset -- except that ``True`` replaces an earlier
|
|
337
|
+
``False``, so ``unique=False`` never switches off ``unique=True`` set
|
|
338
|
+
elsewhere in the declaration. The result is always a **copy** (carriers
|
|
339
|
+
inside type aliases are shared singletons and are later written to, e.g.
|
|
340
|
+
by ``_durably_set_sa_type``). ``None`` when there is no carrier.
|
|
326
341
|
"""
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
folded = copy.copy(metadata[first])
|
|
332
|
-
for index in carrier_indexes[1:]:
|
|
333
|
-
later = metadata[index]
|
|
342
|
+
if not carriers:
|
|
343
|
+
return None
|
|
344
|
+
folded = copy.copy(carriers[0])
|
|
345
|
+
for later in carriers[1:]:
|
|
334
346
|
for fim_field in dataclasses.fields(later):
|
|
335
347
|
value = getattr(later, fim_field.name)
|
|
336
348
|
if value is _FIM_UNSET_SA_TYPE:
|
|
@@ -338,49 +350,46 @@ def _fold_column_carriers(metadata: list[Any]) -> list[Any]:
|
|
|
338
350
|
current = getattr(folded, fim_field.name)
|
|
339
351
|
if current is _FIM_UNSET_SA_TYPE or (current is False and value is True):
|
|
340
352
|
setattr(folded, fim_field.name, value)
|
|
341
|
-
|
|
342
|
-
return [folded if i == first else m for i, m in enumerate(metadata) if i not in dropped]
|
|
353
|
+
return folded
|
|
343
354
|
|
|
344
355
|
|
|
345
356
|
def _find_field_info_in_annotated(annotation: Any) -> SQLModelFieldInfo | None:
|
|
346
357
|
"""
|
|
347
|
-
|
|
358
|
+
Build a right-hand ``Field`` for ``Annotated[T, Field(...)]`` that changes nothing Pydantic resolves.
|
|
348
359
|
|
|
349
360
|
Used by the metaclass sa_type injection loop. When a field is declared
|
|
350
361
|
``Annotated[X, Field(default_factory=list, ...)]`` *without* an explicit
|
|
351
362
|
``= ...`` assignment, ``attrs[field_name]`` is ``Undefined``. Replacing
|
|
352
363
|
it with a fresh ``Field(sa_type=sa_type)`` would discard the user's
|
|
353
364
|
Field metadata (``default_factory``, ``max_length``, validators, ...)
|
|
354
|
-
that lives inside the Annotated args.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
365
|
+
that lives inside the Annotated args. The bug only surfaces after 2+
|
|
366
|
+
levels of inheritance: single-class instantiation goes through
|
|
367
|
+
Pydantic's native Annotated path and works, but child classes rebuild
|
|
368
|
+
``model_fields`` from the clobbered ``attrs`` and the field becomes
|
|
369
|
+
silently ``is_required=True``.
|
|
370
|
+
|
|
371
|
+
The returned field carries Pydantic's own resolution of the annotation
|
|
372
|
+
(every ``Field`` in it merged, later ones overriding earlier ones,
|
|
373
|
+
``None`` included) with every Pydantic attribute explicitly set, so
|
|
374
|
+
Pydantic, merging it back in as the right-hand side, gets the same field
|
|
375
|
+
as without it. Its only metadata is the folded column carrier, to which
|
|
376
|
+
the caller attaches ``sa_type``; the annotation's constraints stay in the
|
|
377
|
+
annotation. Reached for non-table classes only: a table class's
|
|
378
|
+
annotation has already been stripped of its SQLModel ``Field``s by
|
|
379
|
+
``_recover_annotated_sqlmodel_fields``.
|
|
366
380
|
|
|
367
381
|
:param annotation: Field type annotation
|
|
368
|
-
:returns:
|
|
382
|
+
:returns: A new SQLModelFieldInfo (safe to mutate), or None
|
|
369
383
|
"""
|
|
370
384
|
if get_origin(annotation) is not typing.Annotated:
|
|
371
385
|
return None
|
|
372
|
-
args = get_args(annotation)
|
|
373
|
-
if len(args) < 2:
|
|
374
|
-
return None
|
|
375
386
|
sqlmodel_fis: list[SQLModelFieldInfo] = [
|
|
376
|
-
arg for arg in
|
|
387
|
+
arg for arg in get_args(annotation)[1:] if isinstance(arg, SQLModelFieldInfo)
|
|
377
388
|
]
|
|
378
389
|
if not sqlmodel_fis:
|
|
379
390
|
return None
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
_merge_field_info_attrs(merged, extra_fi)
|
|
383
|
-
return merged
|
|
391
|
+
resolved = FieldInfo.from_annotation(annotation)
|
|
392
|
+
return _inherit_resolved_field(sqlmodel_fis, resolved, list(resolved.metadata))
|
|
384
393
|
|
|
385
394
|
|
|
386
395
|
def _durably_set_sa_type(field_info: Any, sa_type: Any) -> None:
|
|
@@ -835,12 +844,6 @@ def _recover_annotated_sqlmodel_fields(
|
|
|
835
844
|
if not sqlmodel_fis:
|
|
836
845
|
continue
|
|
837
846
|
|
|
838
|
-
# Merge multiple FieldInfo: shallow-copy first then merge to avoid mutating
|
|
839
|
-
# shared Annotated metadata singletons (e.g. Str64 = Annotated[str, Field(max_length=64)])
|
|
840
|
-
sqlmodel_fi = copy.copy(sqlmodel_fis[0])
|
|
841
|
-
for extra_fi in sqlmodel_fis[1:]:
|
|
842
|
-
_merge_field_info_attrs(sqlmodel_fi, extra_fi)
|
|
843
|
-
|
|
844
847
|
# The class annotation without the SQLModel FieldInfo; any other
|
|
845
848
|
# ``Annotated`` metadata (validators, serializers, ...) stays in it.
|
|
846
849
|
base_type = args[0]
|
|
@@ -864,62 +867,48 @@ def _recover_annotated_sqlmodel_fields(
|
|
|
864
867
|
else:
|
|
865
868
|
rebuilt = new_inner
|
|
866
869
|
|
|
867
|
-
#
|
|
870
|
+
# The field is rebuilt from Pydantic's resolution of the declaration --
|
|
871
|
+
# the same ``FieldInfo`` a non-table class gets for it (see
|
|
872
|
+
# ``_inherit_resolved_field``): its Pydantic attributes follow that
|
|
873
|
+
# resolution, ``None`` included; only the column attributes of every
|
|
874
|
+
# ``Field`` of the declaration are folded together.
|
|
868
875
|
existing_default = attrs.get(field_name, Undefined)
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
#
|
|
879
|
-
#
|
|
880
|
-
# Pydantic
|
|
881
|
-
# non-table class (``from_annotated_attribute`` is the step that
|
|
882
|
-
# builds ``model_fields`` entries), so the field is then treated like
|
|
883
|
-
# an inherited one: its Pydantic attributes follow that resolution,
|
|
884
|
-
# ``None`` included (``Field(alias=None)`` clears the alias's
|
|
885
|
-
# ``alias``, as in plain SQLModel and in a table class inheriting
|
|
886
|
-
# the same declaration); only the column attributes of the alias
|
|
887
|
-
# and the right-hand ``Field`` are folded together (see
|
|
888
|
-
# ``_inherit_resolved_field``).
|
|
876
|
+
base_fi = next(
|
|
877
|
+
(
|
|
878
|
+
base_fields[field_name]
|
|
879
|
+
for base in bases
|
|
880
|
+
if (base_fields := getattr(base, 'model_fields', None)) and field_name in base_fields
|
|
881
|
+
),
|
|
882
|
+
None,
|
|
883
|
+
)
|
|
884
|
+
if existing_default is not Undefined:
|
|
885
|
+
# A right-hand side in the class body: ``= Field(...)`` or a plain
|
|
886
|
+
# value (``= 0``, ``= None``). ``from_annotated_attribute`` is the
|
|
887
|
+
# step Pydantic uses to build ``model_fields`` entries.
|
|
889
888
|
resolved = FieldInfo.from_annotated_attribute(field_type, existing_default)
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
None,
|
|
901
|
-
)
|
|
902
|
-
if base_fi is not None and field_name not in own_names:
|
|
903
|
-
# An inherited field (the class body does not declare it). Its
|
|
904
|
-
# right-hand ``= Field(...)`` lives in the base class, not in
|
|
905
|
-
# ``attrs``; without this branch the injected
|
|
906
|
-
# ``attrs[field_name]`` replaces the inherited field with the
|
|
907
|
-
# alias's ``Field`` alone, and a column declared
|
|
908
|
-
# ``Field(index=True)`` in a base class loses its index.
|
|
909
|
-
sqlmodel_fi = _inherit_resolved_field(
|
|
910
|
-
sqlmodel_fi, base_fi, FieldInfo.from_annotation(rebuilt).metadata,
|
|
911
|
-
)
|
|
912
|
-
elif (
|
|
889
|
+
elif base_fi is not None and field_name not in own_names:
|
|
890
|
+
# An inherited field (the class body does not declare it). Its
|
|
891
|
+
# right-hand side lives in the base class, not in ``attrs``: the
|
|
892
|
+
# base's resolved field is the declaration's resolution. Without it
|
|
893
|
+
# a column declared ``Field(index=True)`` in a base class loses its
|
|
894
|
+
# index.
|
|
895
|
+
resolved = base_fi
|
|
896
|
+
else:
|
|
897
|
+
resolved = FieldInfo.from_annotation(field_type)
|
|
898
|
+
if (
|
|
913
899
|
base_fi is not None
|
|
914
|
-
and
|
|
915
|
-
and
|
|
900
|
+
and resolved.default is Undefined
|
|
901
|
+
and resolved.default_factory is None
|
|
916
902
|
):
|
|
917
903
|
# Re-declared in the class body without a right-hand side:
|
|
918
|
-
#
|
|
904
|
+
# the base's default is inherited.
|
|
919
905
|
if base_fi.default is not Undefined:
|
|
920
|
-
|
|
906
|
+
resolved.default = base_fi.default
|
|
921
907
|
elif base_fi.default_factory is not None:
|
|
922
|
-
|
|
908
|
+
resolved.default_factory = base_fi.default_factory
|
|
909
|
+
sqlmodel_fi = _inherit_resolved_field(
|
|
910
|
+
sqlmodel_fis, resolved, FieldInfo.from_annotation(rebuilt).metadata,
|
|
911
|
+
)
|
|
923
912
|
|
|
924
913
|
# Inject SQLModel FieldInfo as field default (equivalent to = Field(...) style)
|
|
925
914
|
attrs[field_name] = sqlmodel_fi
|