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