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.
Files changed (321) hide show
  1. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/.github/workflows/ci.yml +146 -100
  2. sqlmodel_ext-0.5.1/CHANGELOG.md +263 -0
  3. sqlmodel_ext-0.5.1/PKG-INFO +1934 -0
  4. sqlmodel_ext-0.5.1/README.md +1878 -0
  5. sqlmodel_ext-0.5.1/README_zh.md +1878 -0
  6. sqlmodel_ext-0.5.1/ai-rules/AGENTS.md +286 -0
  7. sqlmodel_ext-0.5.1/ai-rules/CLAUDE.md +6 -0
  8. sqlmodel_ext-0.5.1/ai-rules/README.md +58 -0
  9. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/en.mts +126 -88
  10. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/zh.mts +126 -88
  11. sqlmodel_ext-0.5.1/docs/en/explanation/cached-table.md +189 -0
  12. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/cascade-delete-semantics.md +155 -153
  13. sqlmodel_ext-0.5.1/docs/en/explanation/crud-pipeline.md +279 -0
  14. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/index.md +48 -41
  15. sqlmodel_ext-0.5.1/docs/en/explanation/metaclass.md +358 -0
  16. sqlmodel_ext-0.5.1/docs/en/explanation/optimistic-lock.md +160 -0
  17. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/polymorphic-internals.md +242 -234
  18. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/prerequisites.md +187 -158
  19. sqlmodel_ext-0.5.1/docs/en/explanation/relation-load-checker.md +184 -0
  20. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/explanation/relation-preload.md +224 -197
  21. sqlmodel_ext-0.5.1/docs/en/explanation/single-source-of-truth.md +138 -0
  22. sqlmodel_ext-0.5.1/docs/en/explanation/transactional-cache-transparency.md +145 -0
  23. sqlmodel_ext-0.5.1/docs/en/explanation/unset-three-state.md +317 -0
  24. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/cached-table.md +17 -17
  25. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/crud.md +15 -15
  26. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/field-types.md +13 -13
  27. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/index.md +13 -13
  28. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/optimistic-lock.md +14 -14
  29. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/pagination.md +14 -14
  30. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/polymorphic.md +17 -17
  31. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/relation-load-checker.md +16 -16
  32. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/guide/relation-preload.md +17 -17
  33. sqlmodel_ext-0.5.1/docs/en/how-to/aggregate-queries.md +133 -0
  34. sqlmodel_ext-0.5.1/docs/en/how-to/cache-queries.md +177 -0
  35. sqlmodel_ext-0.5.1/docs/en/how-to/check-partial-dtos.md +175 -0
  36. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/configure-cascade-delete.md +234 -234
  37. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/define-jti-models.md +122 -120
  38. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/define-sti-models.md +15 -4
  39. sqlmodel_ext-0.5.1/docs/en/how-to/enforce-locking-and-isolation.md +227 -0
  40. sqlmodel_ext-0.5.1/docs/en/how-to/extra-mixins.md +256 -0
  41. sqlmodel_ext-0.5.1/docs/en/how-to/handle-concurrent-updates.md +123 -0
  42. sqlmodel_ext-0.5.1/docs/en/how-to/handle-referenced-deletes.md +86 -0
  43. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/index.md +57 -41
  44. sqlmodel_ext-0.5.1/docs/en/how-to/integrate-with-fastapi.md +179 -0
  45. sqlmodel_ext-0.5.1/docs/en/how-to/keyset-pagination.md +102 -0
  46. sqlmodel_ext-0.5.1/docs/en/how-to/migrate-to-0-5.md +319 -0
  47. sqlmodel_ext-0.5.1/docs/en/how-to/paginate-a-list-endpoint.md +102 -0
  48. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/prevent-missing-greenlet.md +45 -2
  49. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/how-to/release-connection-during-long-io.md +183 -181
  50. sqlmodel_ext-0.5.1/docs/en/how-to/type-check-with-basedpyright.md +258 -0
  51. sqlmodel_ext-0.5.1/docs/en/how-to/use-with-ai-assistants.md +102 -0
  52. sqlmodel_ext-0.5.1/docs/en/how-to/write-patch-endpoints.md +223 -0
  53. sqlmodel_ext-0.5.1/docs/en/index.md +76 -0
  54. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/cached-table.md +13 -13
  55. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/crud.md +15 -15
  56. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/index.md +13 -13
  57. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/metaclass.md +13 -13
  58. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/optimistic-lock.md +13 -13
  59. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/polymorphic.md +13 -13
  60. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/prerequisites.md +13 -13
  61. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/relation-load-checker.md +13 -13
  62. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/internals/relation-preload.md +13 -13
  63. sqlmodel_ext-0.5.1/docs/en/reference/base-classes.md +225 -0
  64. sqlmodel_ext-0.5.1/docs/en/reference/crud-methods.md +392 -0
  65. sqlmodel_ext-0.5.1/docs/en/reference/decorators.md +260 -0
  66. sqlmodel_ext-0.5.1/docs/en/reference/field-types.md +410 -0
  67. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/reference/index.md +54 -54
  68. sqlmodel_ext-0.5.1/docs/en/reference/mixins.md +404 -0
  69. sqlmodel_ext-0.5.1/docs/en/reference/pagination-types.md +178 -0
  70. sqlmodel_ext-0.5.1/docs/en/tutorials/01-getting-started.md +263 -0
  71. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/tutorials/02-building-a-blog-api.md +45 -15
  72. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/tutorials/03-adding-redis-cache.md +22 -11
  73. sqlmodel_ext-0.5.1/docs/explanation/cached-table.md +189 -0
  74. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/cascade-delete-semantics.md +155 -153
  75. sqlmodel_ext-0.5.1/docs/explanation/crud-pipeline.md +279 -0
  76. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/index.md +48 -41
  77. sqlmodel_ext-0.5.1/docs/explanation/metaclass.md +358 -0
  78. sqlmodel_ext-0.5.1/docs/explanation/optimistic-lock.md +160 -0
  79. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/polymorphic-internals.md +231 -223
  80. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/prerequisites.md +187 -158
  81. sqlmodel_ext-0.5.1/docs/explanation/relation-load-checker.md +184 -0
  82. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/explanation/relation-preload.md +224 -197
  83. sqlmodel_ext-0.5.1/docs/explanation/single-source-of-truth.md +138 -0
  84. sqlmodel_ext-0.5.1/docs/explanation/transactional-cache-transparency.md +145 -0
  85. sqlmodel_ext-0.5.1/docs/explanation/unset-three-state.md +317 -0
  86. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/cached-table.md +17 -17
  87. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/crud.md +15 -15
  88. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/field-types.md +13 -13
  89. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/index.md +13 -13
  90. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/optimistic-lock.md +14 -14
  91. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/pagination.md +14 -14
  92. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/polymorphic.md +17 -17
  93. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/relation-load-checker.md +16 -16
  94. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/guide/relation-preload.md +17 -17
  95. sqlmodel_ext-0.5.1/docs/how-to/aggregate-queries.md +133 -0
  96. sqlmodel_ext-0.5.1/docs/how-to/cache-queries.md +177 -0
  97. sqlmodel_ext-0.5.1/docs/how-to/check-partial-dtos.md +175 -0
  98. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/configure-cascade-delete.md +234 -234
  99. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/define-jti-models.md +122 -120
  100. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/define-sti-models.md +15 -4
  101. sqlmodel_ext-0.5.1/docs/how-to/enforce-locking-and-isolation.md +227 -0
  102. sqlmodel_ext-0.5.1/docs/how-to/extra-mixins.md +256 -0
  103. sqlmodel_ext-0.5.1/docs/how-to/handle-concurrent-updates.md +123 -0
  104. sqlmodel_ext-0.5.1/docs/how-to/handle-referenced-deletes.md +86 -0
  105. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/index.md +57 -41
  106. sqlmodel_ext-0.5.1/docs/how-to/integrate-with-fastapi.md +179 -0
  107. sqlmodel_ext-0.5.1/docs/how-to/keyset-pagination.md +102 -0
  108. sqlmodel_ext-0.5.1/docs/how-to/migrate-to-0-5.md +319 -0
  109. sqlmodel_ext-0.5.1/docs/how-to/paginate-a-list-endpoint.md +102 -0
  110. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/prevent-missing-greenlet.md +45 -2
  111. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/how-to/release-connection-during-long-io.md +183 -181
  112. sqlmodel_ext-0.5.1/docs/how-to/type-check-with-basedpyright.md +258 -0
  113. sqlmodel_ext-0.5.1/docs/how-to/use-with-ai-assistants.md +102 -0
  114. sqlmodel_ext-0.5.1/docs/how-to/write-patch-endpoints.md +223 -0
  115. sqlmodel_ext-0.5.1/docs/index.md +76 -0
  116. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/cached-table.md +13 -13
  117. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/crud.md +15 -15
  118. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/index.md +13 -13
  119. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/metaclass.md +13 -13
  120. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/optimistic-lock.md +13 -13
  121. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/polymorphic.md +13 -13
  122. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/prerequisites.md +13 -13
  123. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/relation-load-checker.md +13 -13
  124. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/internals/relation-preload.md +13 -13
  125. sqlmodel_ext-0.5.1/docs/reference/base-classes.md +224 -0
  126. sqlmodel_ext-0.5.1/docs/reference/crud-methods.md +392 -0
  127. sqlmodel_ext-0.5.1/docs/reference/decorators.md +260 -0
  128. sqlmodel_ext-0.5.1/docs/reference/field-types.md +410 -0
  129. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/reference/index.md +54 -54
  130. sqlmodel_ext-0.5.1/docs/reference/mixins.md +404 -0
  131. sqlmodel_ext-0.5.1/docs/reference/pagination-types.md +178 -0
  132. sqlmodel_ext-0.5.1/docs/tutorials/01-getting-started.md +263 -0
  133. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/tutorials/02-building-a-blog-api.md +45 -14
  134. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/tutorials/03-adding-redis-cache.md +22 -11
  135. sqlmodel_ext-0.5.1/examples/01_unset_patch.py +195 -0
  136. sqlmodel_ext-0.5.1/examples/02_single_source_types.py +104 -0
  137. sqlmodel_ext-0.5.1/examples/03_keyset_pagination.py +96 -0
  138. sqlmodel_ext-0.5.1/examples/04_locking_and_isolation.py +155 -0
  139. sqlmodel_ext-0.5.1/examples/05_aggregates.py +91 -0
  140. sqlmodel_ext-0.5.1/examples/06_referenced_delete.py +118 -0
  141. sqlmodel_ext-0.5.1/examples/07_transactional_cache.py +108 -0
  142. sqlmodel_ext-0.5.1/examples/08_optimistic_lock.py +98 -0
  143. sqlmodel_ext-0.5.1/examples/09_relation_preload_bulk.py +117 -0
  144. sqlmodel_ext-0.5.1/examples/10_typed_select.py +103 -0
  145. sqlmodel_ext-0.5.1/examples/11_type_errors_caught_by_basedpyright.py +80 -0
  146. sqlmodel_ext-0.5.1/examples/README.md +154 -0
  147. sqlmodel_ext-0.5.1/examples/check_derived_demo/pyrightconfig.json +4 -0
  148. sqlmodel_ext-0.5.1/examples/check_derived_demo/shop/__init__.py +7 -0
  149. sqlmodel_ext-0.5.1/examples/check_derived_demo/shop/handlers.py +16 -0
  150. sqlmodel_ext-0.5.1/examples/check_derived_demo/shop/models.py +29 -0
  151. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/examples/consumer_integration/run.py +29 -13
  152. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/pyproject.toml +18 -3
  153. sqlmodel_ext-0.5.1/pyrightconfig.json +48 -0
  154. sqlmodel_ext-0.5.1/src/sqlmodel_ext/__init__.py +254 -0
  155. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/_compat.py +4 -2
  156. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/_exceptions.py +1 -1
  157. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/_sa_type.py +20 -8
  158. sqlmodel_ext-0.5.1/src/sqlmodel_ext/_type_unwrap.py +117 -0
  159. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/base.py +963 -302
  160. sqlmodel_ext-0.5.1/src/sqlmodel_ext/check_derived.py +565 -0
  161. sqlmodel_ext-0.5.1/src/sqlmodel_ext/constants.py +76 -0
  162. sqlmodel_ext-0.5.1/src/sqlmodel_ext/dependencies.py +178 -0
  163. sqlmodel_ext-0.5.1/src/sqlmodel_ext/derived_decls.py +818 -0
  164. sqlmodel_ext-0.5.1/src/sqlmodel_ext/field_types/__init__.py +997 -0
  165. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/_internal/path.py +24 -12
  166. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/postgresql/__init__.py +16 -7
  167. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/postgresql/array.py +232 -218
  168. sqlmodel_ext-0.5.1/src/sqlmodel_ext/field_types/dialects/postgresql/jsonb_types.py +266 -0
  169. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/postgresql/numpy_vector.py +43 -27
  170. sqlmodel_ext-0.5.1/src/sqlmodel_ext/field_types/ip_address.py +86 -0
  171. sqlmodel_ext-0.5.1/src/sqlmodel_ext/field_types/mixins/__init__.py +1 -0
  172. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/mixins/module_name_mixin.py +2 -2
  173. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/url.py +6 -6
  174. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/__init__.py +79 -0
  175. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/_uuid.py +69 -0
  176. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/mixins/cached_table.py +969 -391
  177. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/constants.py +17 -0
  178. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/exceptions.py +84 -0
  179. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/migration_cache_invalidation.py +243 -0
  180. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/mixin_table_scan.py +96 -0
  181. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/optimistic_lock.py +161 -0
  182. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/mixins/polymorphic.py +84 -38
  183. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/relation_preload.py +916 -0
  184. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/resource_quota.py +318 -0
  185. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/mixins/table.py +866 -162
  186. sqlmodel_ext-0.5.1/src/sqlmodel_ext/mixins/trgm_searchable.py +230 -0
  187. sqlmodel_ext-0.5.1/src/sqlmodel_ext/pagination.py +291 -0
  188. sqlmodel_ext-0.5.1/src/sqlmodel_ext/relation_load_checker.py +6367 -0
  189. sqlmodel_ext-0.5.1/src/sqlmodel_ext/select.py +466 -0
  190. sqlmodel_ext-0.5.1/src/sqlmodel_ext/session.py +633 -0
  191. sqlmodel_ext-0.5.1/src/sqlmodel_ext/unset.py +189 -0
  192. sqlmodel_ext-0.5.1/tests/__init__.py +0 -0
  193. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/conftest.py +58 -58
  194. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_annotated_default_factory_inheritance.py +307 -194
  195. sqlmodel_ext-0.5.1/tests/test_cached_commit_paths.py +265 -0
  196. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_cached_table.py +239 -7
  197. sqlmodel_ext-0.5.1/tests/test_check_derived.py +239 -0
  198. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_crud_extended.py +244 -4
  199. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_decimal_types.py +431 -256
  200. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_deferred_index.py +50 -50
  201. sqlmodel_ext-0.5.1/tests/test_derived_decls.py +159 -0
  202. sqlmodel_ext-0.5.1/tests/test_enhanced_session.py +526 -0
  203. sqlmodel_ext-0.5.1/tests/test_examples.py +52 -0
  204. sqlmodel_ext-0.5.1/tests/test_field_types_aliases_extra.py +292 -0
  205. sqlmodel_ext-0.5.1/tests/test_field_types_jsonb_limits.py +186 -0
  206. sqlmodel_ext-0.5.1/tests/test_final_051_rereview.py +52 -0
  207. sqlmodel_ext-0.5.1/tests/test_final_051_review.py +262 -0
  208. sqlmodel_ext-0.5.1/tests/test_final_051_review2.py +72 -0
  209. sqlmodel_ext-0.5.1/tests/test_final_check_derived_review2.py +91 -0
  210. sqlmodel_ext-0.5.1/tests/test_final_check_derived_temp_root.py +40 -0
  211. sqlmodel_ext-0.5.1/tests/test_final_nested_duplicate_names.py +38 -0
  212. sqlmodel_ext-0.5.1/tests/test_final_omitted_sentinel_alias_modes.py +38 -0
  213. sqlmodel_ext-0.5.1/tests/test_final_partial_alias_generator.py +20 -0
  214. sqlmodel_ext-0.5.1/tests/test_final_partial_annotated_field_warnings.py +28 -0
  215. sqlmodel_ext-0.5.1/tests/test_final_partial_discriminator.py +42 -0
  216. sqlmodel_ext-0.5.1/tests/test_final_partial_frozen_field.py +21 -0
  217. sqlmodel_ext-0.5.1/tests/test_final_partial_rhs_field_metadata.py +36 -0
  218. sqlmodel_ext-0.5.1/tests/test_final_review4_followup.py +109 -0
  219. sqlmodel_ext-0.5.1/tests/test_final_review5.py +145 -0
  220. sqlmodel_ext-0.5.1/tests/test_inherited_column_attributes.py +666 -0
  221. sqlmodel_ext-0.5.1/tests/test_metaclass_extra.py +396 -0
  222. sqlmodel_ext-0.5.1/tests/test_mixins_extra.py +323 -0
  223. sqlmodel_ext-0.5.1/tests/test_optimistic_lock.py +243 -0
  224. sqlmodel_ext-0.5.1/tests/test_pagination.py +492 -0
  225. sqlmodel_ext-0.5.1/tests/test_partial.py +326 -0
  226. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_pg_dialect_types.py +18 -13
  227. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_polymorphic_jti.py +39 -3
  228. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_polymorphic_sti_query.py +55 -1
  229. sqlmodel_ext-0.5.1/tests/test_query_dependency.py +326 -0
  230. sqlmodel_ext-0.5.1/tests/test_query_dependency_metadata.py +154 -0
  231. sqlmodel_ext-0.5.1/tests/test_relation_load_checker_load_resolution.py +1555 -0
  232. sqlmodel_ext-0.5.1/tests/test_relation_load_checker_sessions.py +563 -0
  233. sqlmodel_ext-0.5.1/tests/test_relation_preload.py +499 -0
  234. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_relationship_pep604.py +115 -115
  235. sqlmodel_ext-0.5.1/tests/test_review_mixed_version_cache.py +59 -0
  236. sqlmodel_ext-0.5.1/tests/test_review_raw_insert_cache.py +50 -0
  237. sqlmodel_ext-0.5.1/tests/test_review_raw_insert_edges.py +68 -0
  238. sqlmodel_ext-0.5.1/tests/test_review_raw_insert_late_model.py +29 -0
  239. sqlmodel_ext-0.5.1/tests/test_review_raw_insert_sti.py +56 -0
  240. sqlmodel_ext-0.5.1/tests/test_select.py +156 -0
  241. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_smoke_imports.py +265 -157
  242. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_sti_default_isolation.py +132 -132
  243. sqlmodel_ext-0.5.1/tests/test_unset.py +241 -0
  244. sqlmodel_ext-0.4.2/PKG-INFO +0 -1518
  245. sqlmodel_ext-0.4.2/README.md +0 -1467
  246. sqlmodel_ext-0.4.2/README_zh.md +0 -1383
  247. sqlmodel_ext-0.4.2/docs/en/explanation/cached-table.md +0 -248
  248. sqlmodel_ext-0.4.2/docs/en/explanation/crud-pipeline.md +0 -288
  249. sqlmodel_ext-0.4.2/docs/en/explanation/metaclass.md +0 -375
  250. sqlmodel_ext-0.4.2/docs/en/explanation/optimistic-lock.md +0 -120
  251. sqlmodel_ext-0.4.2/docs/en/explanation/relation-load-checker.md +0 -148
  252. sqlmodel_ext-0.4.2/docs/en/how-to/cache-queries.md +0 -143
  253. sqlmodel_ext-0.4.2/docs/en/how-to/handle-concurrent-updates.md +0 -94
  254. sqlmodel_ext-0.4.2/docs/en/how-to/integrate-with-fastapi.md +0 -119
  255. sqlmodel_ext-0.4.2/docs/en/how-to/paginate-a-list-endpoint.md +0 -79
  256. sqlmodel_ext-0.4.2/docs/en/index.md +0 -45
  257. sqlmodel_ext-0.4.2/docs/en/reference/base-classes.md +0 -143
  258. sqlmodel_ext-0.4.2/docs/en/reference/crud-methods.md +0 -281
  259. sqlmodel_ext-0.4.2/docs/en/reference/decorators.md +0 -164
  260. sqlmodel_ext-0.4.2/docs/en/reference/field-types.md +0 -279
  261. sqlmodel_ext-0.4.2/docs/en/reference/mixins.md +0 -257
  262. sqlmodel_ext-0.4.2/docs/en/reference/pagination-types.md +0 -113
  263. sqlmodel_ext-0.4.2/docs/en/tutorials/01-getting-started.md +0 -175
  264. sqlmodel_ext-0.4.2/docs/explanation/cached-table.md +0 -244
  265. sqlmodel_ext-0.4.2/docs/explanation/crud-pipeline.md +0 -288
  266. sqlmodel_ext-0.4.2/docs/explanation/metaclass.md +0 -351
  267. sqlmodel_ext-0.4.2/docs/explanation/optimistic-lock.md +0 -120
  268. sqlmodel_ext-0.4.2/docs/explanation/relation-load-checker.md +0 -136
  269. sqlmodel_ext-0.4.2/docs/how-to/cache-queries.md +0 -143
  270. sqlmodel_ext-0.4.2/docs/how-to/handle-concurrent-updates.md +0 -93
  271. sqlmodel_ext-0.4.2/docs/how-to/integrate-with-fastapi.md +0 -119
  272. sqlmodel_ext-0.4.2/docs/how-to/paginate-a-list-endpoint.md +0 -79
  273. sqlmodel_ext-0.4.2/docs/index.md +0 -45
  274. sqlmodel_ext-0.4.2/docs/reference/base-classes.md +0 -140
  275. sqlmodel_ext-0.4.2/docs/reference/crud-methods.md +0 -279
  276. sqlmodel_ext-0.4.2/docs/reference/decorators.md +0 -164
  277. sqlmodel_ext-0.4.2/docs/reference/field-types.md +0 -279
  278. sqlmodel_ext-0.4.2/docs/reference/mixins.md +0 -252
  279. sqlmodel_ext-0.4.2/docs/reference/pagination-types.md +0 -113
  280. sqlmodel_ext-0.4.2/docs/tutorials/01-getting-started.md +0 -175
  281. sqlmodel_ext-0.4.2/src/sqlmodel_ext/__init__.py +0 -173
  282. sqlmodel_ext-0.4.2/src/sqlmodel_ext/field_types/__init__.py +0 -560
  283. sqlmodel_ext-0.4.2/src/sqlmodel_ext/field_types/dialects/postgresql/jsonb_types.py +0 -178
  284. sqlmodel_ext-0.4.2/src/sqlmodel_ext/field_types/ip_address.py +0 -50
  285. sqlmodel_ext-0.4.2/src/sqlmodel_ext/field_types/mixins/__init__.py +0 -1
  286. sqlmodel_ext-0.4.2/src/sqlmodel_ext/mixins/__init__.py +0 -39
  287. sqlmodel_ext-0.4.2/src/sqlmodel_ext/mixins/optimistic_lock.py +0 -85
  288. sqlmodel_ext-0.4.2/src/sqlmodel_ext/mixins/relation_preload.py +0 -436
  289. sqlmodel_ext-0.4.2/src/sqlmodel_ext/pagination.py +0 -127
  290. sqlmodel_ext-0.4.2/src/sqlmodel_ext/relation_load_checker.py +0 -3825
  291. sqlmodel_ext-0.4.2/src/sqlmodel_ext/session.py +0 -105
  292. sqlmodel_ext-0.4.2/tests/test_all_fields_optional_constraints.py +0 -76
  293. sqlmodel_ext-0.4.2/tests/test_enhanced_session.py +0 -87
  294. sqlmodel_ext-0.4.2/tests/test_metaclass_extra.py +0 -194
  295. sqlmodel_ext-0.4.2/tests/test_optimistic_lock.py +0 -190
  296. sqlmodel_ext-0.4.2/tests/test_pagination.py +0 -259
  297. sqlmodel_ext-0.4.2/tests/test_relation_preload.py +0 -240
  298. sqlmodel_ext-0.4.2/uv.lock +0 -585
  299. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/.gitignore +0 -0
  300. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/LICENSE +0 -0
  301. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/config.mts +0 -0
  302. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/theme/index.ts +0 -0
  303. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/.vitepress/theme/style/custom.css +0 -0
  304. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/en/tutorials/index.md +0 -0
  305. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/docs/tutorials/index.md +0 -0
  306. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/package.json +0 -0
  307. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/pnpm-lock.yaml +0 -0
  308. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/_utils.py +0 -0
  309. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/_internal/__init__.py +0 -0
  310. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/_ssrf.py +0 -0
  311. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/__init__.py +0 -0
  312. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/field_types/dialects/postgresql/exceptions.py +0 -0
  313. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/src/sqlmodel_ext/mixins/info_response.py +0 -0
  314. /sqlmodel_ext-0.4.2/tests/__init__.py → /sqlmodel_ext-0.5.1/src/sqlmodel_ext/py.typed +0 -0
  315. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/_models.py +0 -0
  316. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_array_explicit_field_sa_type.py +0 -0
  317. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_basic_crud.py +0 -0
  318. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_field_types_misc.py +0 -0
  319. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_field_types_str_num.py +0 -0
  320. {sqlmodel_ext-0.4.2 → sqlmodel_ext-0.5.1}/tests/test_field_types_url_ssrf.py +0 -0
  321. {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 2: build wheel + sdist, then install the wheel into a clean
54
- # virtualenv with zero dev deps and run the consumer integration
55
- # script. This simulates an external PyPI consumer.
56
- # -------------------------------------------------------------------
57
- consumer-integration:
58
- name: consumer integration (py${{ matrix.python-version }})
59
- runs-on: ubuntu-latest
60
- needs: test
61
- strategy:
62
- fail-fast: false
63
- matrix:
64
- python-version: ["3.12", "3.13", "3.14"]
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
- - name: Set up Python ${{ matrix.python-version }}
75
- run: uv python install ${{ matrix.python-version }}
76
-
77
- - name: Build wheel and sdist
78
- run: uv tool run --from build python -m build
79
-
80
- - name: Create isolated consumer venv
81
- run: uv venv --python ${{ matrix.python-version }} /tmp/consumer-env
82
-
83
- - name: Install built wheel (no dev deps, only the wheel + aiosqlite driver)
84
- run: uv pip install --python /tmp/consumer-env/bin/python dist/*.whl aiosqlite
85
-
86
- - name: Confirm installed version matches wheel
87
- run: |
88
- /tmp/consumer-env/bin/python -c "import sqlmodel_ext; print('installed:', sqlmodel_ext.__version__ if hasattr(sqlmodel_ext, '__version__') else '(no __version__)')"
89
-
90
- - name: Run consumer integration scenario
91
- run: /tmp/consumer-env/bin/python examples/consumer_integration/run.py
92
-
93
- - name: Upload distribution artifacts
94
- if: matrix.python-version == '3.12'
95
- uses: actions/upload-artifact@v4
96
- with:
97
- name: dist
98
- path: dist/
99
- if-no-files-found: error
100
- retention-days: 14
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).