sqlmodel-ext 0.5.4__tar.gz → 0.5.5__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 (269) hide show
  1. sqlmodel_ext-0.5.5/.gitattributes +5 -0
  2. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/CHANGELOG.md +42 -0
  3. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/PKG-INFO +1 -1
  4. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/prerequisites.md +1 -1
  5. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/unset-three-state.md +3 -3
  6. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/reference/base-classes.md +1 -1
  7. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/reference/crud-methods.md +2 -1
  8. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/reference/field-types.md +2 -1
  9. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/reference/pagination-types.md +9 -3
  10. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/prerequisites.md +1 -1
  11. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/unset-three-state.md +3 -3
  12. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/reference/base-classes.md +1 -1
  13. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/reference/crud-methods.md +2 -1
  14. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/reference/field-types.md +2 -1
  15. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/reference/pagination-types.md +9 -3
  16. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/pyproject.toml +4 -3
  17. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/__init__.py +255 -254
  18. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/_sa_type.py +150 -150
  19. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/__init__.py +28 -1
  20. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/cached_table.py +2747 -2739
  21. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/polymorphic.py +848 -848
  22. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/relation_preload.py +3 -1
  23. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/table.py +54 -8
  24. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/pagination.py +11 -5
  25. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/unset.py +7 -6
  26. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_decimal_types.py +28 -10
  27. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_pagination.py +2 -1
  28. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_partial.py +7 -4
  29. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_unset.py +17 -1
  30. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/.github/workflows/ci.yml +0 -0
  31. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/.github/workflows/release.yml +0 -0
  32. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/.gitignore +0 -0
  33. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/LICENSE +0 -0
  34. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/README.md +0 -0
  35. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/README_zh.md +0 -0
  36. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/ai-rules/AGENTS.md +0 -0
  37. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/ai-rules/CLAUDE.md +0 -0
  38. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/ai-rules/README.md +0 -0
  39. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/.vitepress/config.mts +0 -0
  40. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/.vitepress/en.mts +0 -0
  41. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/.vitepress/theme/index.ts +0 -0
  42. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/.vitepress/theme/style/custom.css +0 -0
  43. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/.vitepress/zh.mts +0 -0
  44. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/cached-table.md +0 -0
  45. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/cascade-delete-semantics.md +0 -0
  46. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/crud-pipeline.md +0 -0
  47. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/index.md +0 -0
  48. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/metaclass.md +0 -0
  49. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/optimistic-lock.md +0 -0
  50. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/polymorphic-internals.md +0 -0
  51. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/relation-load-checker.md +0 -0
  52. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/relation-preload.md +0 -0
  53. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/single-source-of-truth.md +0 -0
  54. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/explanation/transactional-cache-transparency.md +0 -0
  55. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/guide/cached-table.md +0 -0
  56. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/guide/crud.md +0 -0
  57. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/guide/field-types.md +0 -0
  58. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/guide/index.md +0 -0
  59. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/guide/optimistic-lock.md +0 -0
  60. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/guide/pagination.md +0 -0
  61. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/guide/polymorphic.md +0 -0
  62. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/guide/relation-load-checker.md +0 -0
  63. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/guide/relation-preload.md +0 -0
  64. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/aggregate-queries.md +0 -0
  65. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/cache-queries.md +0 -0
  66. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/check-partial-dtos.md +0 -0
  67. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/configure-cascade-delete.md +0 -0
  68. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/define-jti-models.md +0 -0
  69. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/define-sti-models.md +0 -0
  70. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/enforce-locking-and-isolation.md +0 -0
  71. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/extra-mixins.md +0 -0
  72. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/handle-concurrent-updates.md +0 -0
  73. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/handle-referenced-deletes.md +0 -0
  74. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/index.md +0 -0
  75. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/integrate-with-fastapi.md +0 -0
  76. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/keyset-pagination.md +0 -0
  77. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/migrate-to-0-5.md +0 -0
  78. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/paginate-a-list-endpoint.md +0 -0
  79. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/prevent-missing-greenlet.md +0 -0
  80. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/release-connection-during-long-io.md +0 -0
  81. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/type-check-with-basedpyright.md +0 -0
  82. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/use-with-ai-assistants.md +0 -0
  83. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/how-to/write-patch-endpoints.md +0 -0
  84. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/index.md +0 -0
  85. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/internals/cached-table.md +0 -0
  86. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/internals/crud.md +0 -0
  87. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/internals/index.md +0 -0
  88. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/internals/metaclass.md +0 -0
  89. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/internals/optimistic-lock.md +0 -0
  90. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/internals/polymorphic.md +0 -0
  91. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/internals/prerequisites.md +0 -0
  92. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/internals/relation-load-checker.md +0 -0
  93. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/internals/relation-preload.md +0 -0
  94. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/reference/decorators.md +0 -0
  95. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/reference/index.md +0 -0
  96. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/reference/mixins.md +0 -0
  97. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/tutorials/01-getting-started.md +0 -0
  98. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/tutorials/02-building-a-blog-api.md +0 -0
  99. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/tutorials/03-adding-redis-cache.md +0 -0
  100. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/en/tutorials/index.md +0 -0
  101. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/cached-table.md +0 -0
  102. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/cascade-delete-semantics.md +0 -0
  103. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/crud-pipeline.md +0 -0
  104. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/index.md +0 -0
  105. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/metaclass.md +0 -0
  106. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/optimistic-lock.md +0 -0
  107. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/polymorphic-internals.md +0 -0
  108. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/relation-load-checker.md +0 -0
  109. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/relation-preload.md +0 -0
  110. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/single-source-of-truth.md +0 -0
  111. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/explanation/transactional-cache-transparency.md +0 -0
  112. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/guide/cached-table.md +0 -0
  113. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/guide/crud.md +0 -0
  114. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/guide/field-types.md +0 -0
  115. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/guide/index.md +0 -0
  116. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/guide/optimistic-lock.md +0 -0
  117. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/guide/pagination.md +0 -0
  118. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/guide/polymorphic.md +0 -0
  119. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/guide/relation-load-checker.md +0 -0
  120. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/guide/relation-preload.md +0 -0
  121. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/aggregate-queries.md +0 -0
  122. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/cache-queries.md +0 -0
  123. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/check-partial-dtos.md +0 -0
  124. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/configure-cascade-delete.md +0 -0
  125. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/define-jti-models.md +0 -0
  126. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/define-sti-models.md +0 -0
  127. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/enforce-locking-and-isolation.md +0 -0
  128. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/extra-mixins.md +0 -0
  129. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/handle-concurrent-updates.md +0 -0
  130. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/handle-referenced-deletes.md +0 -0
  131. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/index.md +0 -0
  132. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/integrate-with-fastapi.md +0 -0
  133. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/keyset-pagination.md +0 -0
  134. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/migrate-to-0-5.md +0 -0
  135. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/paginate-a-list-endpoint.md +0 -0
  136. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/prevent-missing-greenlet.md +0 -0
  137. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/release-connection-during-long-io.md +0 -0
  138. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/type-check-with-basedpyright.md +0 -0
  139. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/use-with-ai-assistants.md +0 -0
  140. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/how-to/write-patch-endpoints.md +0 -0
  141. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/index.md +0 -0
  142. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/internals/cached-table.md +0 -0
  143. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/internals/crud.md +0 -0
  144. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/internals/index.md +0 -0
  145. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/internals/metaclass.md +0 -0
  146. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/internals/optimistic-lock.md +0 -0
  147. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/internals/polymorphic.md +0 -0
  148. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/internals/prerequisites.md +0 -0
  149. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/internals/relation-load-checker.md +0 -0
  150. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/internals/relation-preload.md +0 -0
  151. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/reference/decorators.md +0 -0
  152. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/reference/index.md +0 -0
  153. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/reference/mixins.md +0 -0
  154. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/tutorials/01-getting-started.md +0 -0
  155. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/tutorials/02-building-a-blog-api.md +0 -0
  156. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/tutorials/03-adding-redis-cache.md +0 -0
  157. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/docs/tutorials/index.md +0 -0
  158. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/01_unset_patch.py +0 -0
  159. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/02_single_source_types.py +0 -0
  160. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/03_keyset_pagination.py +0 -0
  161. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/04_locking_and_isolation.py +0 -0
  162. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/05_aggregates.py +0 -0
  163. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/06_referenced_delete.py +0 -0
  164. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/07_transactional_cache.py +0 -0
  165. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/08_optimistic_lock.py +0 -0
  166. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/09_relation_preload_bulk.py +0 -0
  167. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/10_typed_select.py +0 -0
  168. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/11_type_errors_caught_by_basedpyright.py +0 -0
  169. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/README.md +0 -0
  170. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/check_derived_demo/pyrightconfig.json +0 -0
  171. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/check_derived_demo/shop/__init__.py +0 -0
  172. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/check_derived_demo/shop/handlers.py +0 -0
  173. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/check_derived_demo/shop/models.py +0 -0
  174. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/examples/consumer_integration/run.py +0 -0
  175. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/package.json +0 -0
  176. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/pnpm-lock.yaml +0 -0
  177. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/pyrightconfig.json +0 -0
  178. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/_compat.py +0 -0
  179. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/_exceptions.py +0 -0
  180. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/_type_unwrap.py +0 -0
  181. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/_utils.py +0 -0
  182. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/base.py +0 -0
  183. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/check_derived.py +0 -0
  184. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/constants.py +0 -0
  185. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/dependencies.py +0 -0
  186. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/derived_decls.py +0 -0
  187. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/_internal/__init__.py +0 -0
  188. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/_internal/path.py +0 -0
  189. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/_ssrf.py +0 -0
  190. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/dialects/__init__.py +0 -0
  191. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/dialects/postgresql/__init__.py +0 -0
  192. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/dialects/postgresql/array.py +0 -0
  193. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/dialects/postgresql/exceptions.py +0 -0
  194. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/dialects/postgresql/jsonb_types.py +0 -0
  195. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/dialects/postgresql/numpy_vector.py +0 -0
  196. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/ip_address.py +0 -0
  197. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/mixins/__init__.py +0 -0
  198. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/mixins/module_name_mixin.py +0 -0
  199. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/field_types/url.py +0 -0
  200. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/__init__.py +0 -0
  201. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/_uuid.py +0 -0
  202. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/constants.py +0 -0
  203. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/exceptions.py +0 -0
  204. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/info_response.py +0 -0
  205. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/migration_cache_invalidation.py +0 -0
  206. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/mixin_table_scan.py +0 -0
  207. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/optimistic_lock.py +0 -0
  208. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/resource_quota.py +0 -0
  209. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/mixins/trgm_searchable.py +0 -0
  210. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/py.typed +0 -0
  211. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/relation_load_checker.py +0 -0
  212. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/select.py +0 -0
  213. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/src/sqlmodel_ext/session.py +0 -0
  214. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/__init__.py +0 -0
  215. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/_models.py +0 -0
  216. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/conftest.py +0 -0
  217. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_annotated_default_factory_inheritance.py +0 -0
  218. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_array_explicit_field_sa_type.py +0 -0
  219. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_basic_crud.py +0 -0
  220. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_cached_commit_paths.py +0 -0
  221. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_cached_table.py +0 -0
  222. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_check_derived.py +0 -0
  223. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_crud_extended.py +0 -0
  224. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_deferred_index.py +0 -0
  225. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_derived_decls.py +0 -0
  226. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_enhanced_session.py +0 -0
  227. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_examples.py +0 -0
  228. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_field_types_aliases_extra.py +0 -0
  229. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_field_types_jsonb_limits.py +0 -0
  230. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_field_types_misc.py +0 -0
  231. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_field_types_str_num.py +0 -0
  232. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_field_types_url_ssrf.py +0 -0
  233. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_051_rereview.py +0 -0
  234. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_051_review.py +0 -0
  235. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_051_review2.py +0 -0
  236. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_check_derived_review2.py +0 -0
  237. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_check_derived_temp_root.py +0 -0
  238. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_nested_duplicate_names.py +0 -0
  239. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_omitted_sentinel_alias_modes.py +0 -0
  240. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_partial_alias_generator.py +0 -0
  241. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_partial_annotated_field_warnings.py +0 -0
  242. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_partial_discriminator.py +0 -0
  243. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_partial_frozen_field.py +0 -0
  244. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_partial_rhs_field_metadata.py +0 -0
  245. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_review4_followup.py +0 -0
  246. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_final_review5.py +0 -0
  247. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_inherited_column_attributes.py +0 -0
  248. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_metaclass_extra.py +0 -0
  249. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_metaclass_field_resolution_052.py +0 -0
  250. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_mixins_extra.py +0 -0
  251. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_optimistic_lock.py +0 -0
  252. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_pg_dialect_types.py +0 -0
  253. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_polymorphic_jti.py +0 -0
  254. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_polymorphic_sti_query.py +0 -0
  255. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_query_dependency.py +0 -0
  256. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_query_dependency_metadata.py +0 -0
  257. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_relation_load_checker.py +0 -0
  258. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_relation_load_checker_load_resolution.py +0 -0
  259. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_relation_load_checker_sessions.py +0 -0
  260. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_relation_preload.py +0 -0
  261. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_relationship_pep604.py +0 -0
  262. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_review_mixed_version_cache.py +0 -0
  263. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_review_raw_insert_cache.py +0 -0
  264. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_review_raw_insert_edges.py +0 -0
  265. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_review_raw_insert_late_model.py +0 -0
  266. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_review_raw_insert_sti.py +0 -0
  267. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_select.py +0 -0
  268. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_smoke_imports.py +0 -0
  269. {sqlmodel_ext-0.5.4 → sqlmodel_ext-0.5.5}/tests/test_sti_default_isolation.py +0 -0
@@ -0,0 +1,5 @@
1
+ # Store every text file with LF line endings, whatever the contributor's
2
+ # core.autocrlf setting. Without this, files committed from Windows with CRLF
3
+ # stayed CRLF in the repository (git only converts on add when it already
4
+ # considers the file text-normalized).
5
+ * text=auto eol=lf
@@ -7,6 +7,48 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
7
7
  > there is no stability or backward-compatibility guarantee between versions, and
8
8
  > you use it at your own risk. Pin the exact version you depend on.
9
9
 
10
+ ## [0.5.5]
11
+
12
+ ### Added
13
+
14
+ - `TableViewRequest.id_prefix`: list only the rows whose UUID primary key, in canonical text
15
+ form, starts with the given prefix (case-insensitive, surrounding whitespace removed).
16
+ `get()` and `get_with_count()` apply it whenever the `table_view` carries it, and the count
17
+ of `get_with_count()` follows the same filter; a plain `count()` takes it as
18
+ `count(id_prefix=...)`. It is applied as a primary-key range (`id BETWEEN lower AND upper`),
19
+ which uses the primary-key index, rather than `CAST(id AS TEXT) LIKE 'prefix%'`, which scans
20
+ the table. With `query_dependency(TableViewRequest)` every list endpoint gets an `id_prefix`
21
+ query parameter. A model whose primary key is not a UUID raises `ValueError` when it is
22
+ given. Like the time filters, it is not part of the keyset cursor's anchor visibility. The
23
+ query cache key of `CachedTableBaseMixin` includes it.
24
+ - `UUIDPrefix` field type: the beginning of a UUID in canonical text form, 4-36 characters
25
+ after stripping, hex digits in either case, `-` only right after a complete `8-4-4-4-12`
26
+ group. Input that cannot start a UUID (`"zzzz"`, `"0192-f4"`) is rejected instead of
27
+ silently matching nothing.
28
+
29
+ ### Fixed
30
+
31
+ - Importing `sqlmodel_ext` with Pydantic 2.14 emitted `PydanticDeprecatedSince214`:
32
+ `Unset` was imported from `pydantic.experimental.missing_sentinel`, which 2.14 deprecates
33
+ in favor of `pydantic.MISSING`. It is now imported from `pydantic_core`, where every
34
+ supported Pydantic release (2.12 on) defines it; it is the same object, so `Unset is
35
+ pydantic.MISSING` still holds.
36
+ - Type checking against SQLAlchemy 2.1.7+ reported two unnecessary suppressions inside the
37
+ library: 2.1.7 changed the overloads of `sqlalchemy.inspect()` so that a call without
38
+ `raiseerr` returns `Any` instead of `Optional[Any]`. Both call sites now pass
39
+ `raiseerr=True`, which is typed the same way before and after 2.1.7. No runtime change.
40
+
41
+ ### Changed
42
+
43
+ - With Pydantic 2.14, an explicit `null` for an `Unset | T` field that does not accept `None`
44
+ is reported as a single error for the `T` branch (e.g. `string_type`, `loc` ending at the
45
+ field name); 2.12 and 2.13 also report a `missing_sentinel_error` with branch tags in
46
+ `loc`. This is Pydantic's change; the input is rejected either way. The documentation and
47
+ the tests now describe both shapes.
48
+ - Pydantic 2.14.1 checks the integer digits of a `Decimal` regardless of where a
49
+ `BeforeValidator` sits in the `Annotated` metadata. The decimal aliases keep their
50
+ metadata order, which earlier Pydantic releases still need.
51
+
10
52
  ## [0.5.4]
11
53
 
12
54
  ### Changed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sqlmodel-ext
3
- Version: 0.5.4
3
+ Version: 0.5.5
4
4
  Summary: Extended SQLModel infrastructure: smart metaclass, async CRUD mixins, polymorphic inheritance, optimistic locking, relation preloading, and reusable field types.
5
5
  Project-URL: Homepage, https://github.com/Foxerine/sqlmodel-ext
6
6
  Project-URL: Repository, https://github.com/Foxerine/sqlmodel-ext
@@ -150,7 +150,7 @@ That is the basic building block of sqlmodel-ext's "declare once" approach: the
150
150
 
151
151
  ## Sentinel values: when `None` is a real value
152
152
 
153
- A **sentinel** is a unique object whose only job is to mean "a special situation", compared with `is`. Python has used them for a long time (`dataclasses.MISSING`, private `_NOTSET = object()` constants), and [PEP 661](https://peps.python.org/pep-0661/) standardizes the pattern. Pydantic 2.12 ships one as `pydantic.experimental.missing_sentinel.MISSING`, which sqlmodel-ext exports as `Unset`.
153
+ A **sentinel** is a unique object whose only job is to mean "a special situation", compared with `is`. Python has used them for a long time (`dataclasses.MISSING`, private `_NOTSET = object()` constants), and [PEP 661](https://peps.python.org/pep-0661/) standardizes the pattern. Pydantic ships one as `MISSING` (`pydantic.experimental.missing_sentinel.MISSING` since 2.12, `pydantic.MISSING` since 2.14), which sqlmodel-ext exports as `Unset`.
154
154
 
155
155
  Why it matters: in a PATCH body, `None` has to be able to mean "clear this column", so "the field was not sent" needs a different carrier. With `Unset`, the three states — not sent / `null` / a value — become distinguishable:
156
156
 
@@ -38,7 +38,7 @@ Conventions live in people's heads. The one time someone forgets, nothing raises
38
38
  from sqlmodel_ext import Unset
39
39
  ```
40
40
 
41
- `Unset` **is** Pydantic's official `pydantic.experimental.missing_sentinel.MISSING` (a PEP 661-style sentinel); sqlmodel-ext only exports it under another name. Once "not provided" is carried by `Unset`, `None` goes back to being an **ordinary value**.
41
+ `Unset` **is** Pydantic's official `MISSING` sentinel (a PEP 661-style sentinel; `pydantic.MISSING` since Pydantic 2.14, `pydantic.experimental.missing_sentinel.MISSING` in 2.12–2.13); sqlmodel-ext only exports it under another name. Once "not provided" is carried by `Unset`, `None` goes back to being an **ordinary value**.
42
42
 
43
43
  | Annotation | Meaning |
44
44
  |---|---|
@@ -94,7 +94,7 @@ else:
94
94
  - **A value** → validated as usual (length, range and every other constraint is kept).
95
95
 
96
96
  ::: warning Do not rely on the order of errors
97
- `Unset | T` is a union, so a failed validation lists one error per branch in `ValidationError.errors()` (for `title=null` above you get both `missing_sentinel_error` and `string_type`, with branch tags such as `'missing-sentinel'` / `'constrained-str'` in `loc`). The order of union members does not change **which** inputs are accepted or rejected, but it does change the **order** of error entries; do not depend on that order downstream.
97
+ `Unset | T` is a union. With Pydantic 2.12–2.13 a failed validation lists one error per branch in `ValidationError.errors()` (for `title=null` above you get both `missing_sentinel_error` and `string_type`, with branch tags such as `'missing-sentinel'` / `'constrained-str'` in `loc`); from Pydantic 2.14 on the `MISSING` branch is no longer reported, so you get only the `T` branch's error (`string_type`, with `loc` ending at the field name). The order of union members does not change **which** inputs are accepted or rejected, but it does change the **order** of error entries; do not depend on that order downstream.
98
98
  :::
99
99
 
100
100
  ## `partial=True`: derive a PATCH DTO from the base
@@ -312,6 +312,6 @@ In one sentence: **`EXCLUDE_IF_NONE` is an output-format convention; `Unset` is
312
312
 
313
313
  ## Known limitations
314
314
 
315
- - Pydantic still marks the feature experimental (module `pydantic.experimental.missing_sentinel`, available since Pydantic 2.12), hence sqlmodel-ext 0.5 requires `pydantic>=2.12`.
315
+ - The sentinel first shipped in Pydantic 2.12 as experimental (`pydantic.experimental.missing_sentinel.MISSING`), hence sqlmodel-ext 0.5 requires `pydantic>=2.12`. Pydantic 2.14 made it stable as `pydantic.MISSING` and deprecated the experimental path; sqlmodel-ext imports it from `pydantic_core`, which works without a deprecation warning on every supported release.
316
316
  - Static narrowing requires PEP 661 support (versions above); with basedpyright alone, partial-derived fields do not take part in static narrowing — declare them explicitly or use [`check_derived`](/en/how-to/check-partial-dtos) (see above).
317
317
  - In models with `omitted_sentinel` enabled, string fields cannot hold the literal `'__omitted__'`.
@@ -121,7 +121,7 @@ class ToolArguments(SQLModelBase):
121
121
  from sqlmodel_ext import Unset, OMITTED_SENTINEL
122
122
  ```
123
123
 
124
- - `Unset`: Pydantic's official `pydantic.experimental.missing_sentinel.MISSING`, meaning "this field was not provided". Check with `x is Unset`. Fields whose value is `Unset` never appear in `model_dump()` / `model_dump_json()` output. `copy.deepcopy(Unset) is Unset`.
124
+ - `Unset`: Pydantic's official `MISSING` sentinel (`pydantic.MISSING` since Pydantic 2.14, `pydantic.experimental.missing_sentinel.MISSING` before), meaning "this field was not provided". Check with `x is Unset`. Fields whose value is `Unset` never appear in `model_dump()` / `model_dump_json()` output. `copy.deepcopy(Unset) is Unset`.
125
125
  - `OMITTED_SENTINEL`: `'__omitted__'`, the wire sentinel value; it only exists in the JSON Schema and inbound payloads of models with `omitted_sentinel` enabled and is normalized to `Unset` on entry. Application code should never compare against it.
126
126
 
127
127
  Full semantics in [Unset](/en/explanation/unset-three-state).
@@ -280,10 +280,11 @@ async def count(
280
280
  created_after_datetime: datetime | None = None,
281
281
  updated_before_datetime: datetime | None = None,
282
282
  updated_after_datetime: datetime | None = None,
283
+ id_prefix: UUIDPrefix | None = None,
283
284
  ) -> int
284
285
  ```
285
286
 
286
- Returns the number of matching records via `SELECT COUNT(*)`. Passing `distinct_column` switches to `COUNT(DISTINCT col)` (e.g. "distinct active users"). Non-null fields in `time_filter` take precedence over the individually passed time parameters.
287
+ Returns the number of matching records via `SELECT COUNT(*)`. Passing `distinct_column` switches to `COUNT(DISTINCT col)` (e.g. "distinct active users"). Non-null fields in `time_filter` take precedence over the individually passed time parameters. `id_prefix` counts only rows whose UUID primary key starts with the prefix (the same filter as [`TableViewRequest.id_prefix`](/en/reference/pagination-types)); `time_filter` does not carry it, even when a `TableViewRequest` is passed there.
287
288
 
288
289
  ## `distinct_column()`
289
290
 
@@ -37,7 +37,7 @@ from sqlmodel_ext import Str1, Str16, Str24, Str32, Str36, Str48, Str64, Str100,
37
37
  from sqlmodel_ext import (
38
38
  NonEmptyStr64, NonEmptyStr128, NonEmptyStr256,
39
39
  NonEmptyStrippedStr32, NonEmptyStrippedStr64, NonEmptyStrippedStr128, NonEmptyStrippedStr256,
40
- SingleLineStr64, SearchQueryStr64, HttpHeaderName,
40
+ SingleLineStr64, SearchQueryStr64, UUIDPrefix, HttpHeaderName,
41
41
  Sha256Hex, BCP47LanguageCode,
42
42
  )
43
43
  ```
@@ -48,6 +48,7 @@ from sqlmodel_ext import (
48
48
  | `NonEmptyStrippedStr32/64/128/256` | Same as above + `strip_whitespace`, rejects whitespace-only input (`" "` / `"\t"`) |
49
49
  | `SingleLineStr64` | `NonEmptyStrippedStr64` + **single line**: rejects NUL and every line-break character (`\n \v \f \r \x1c \x1d \x1e \x85 
 
`, derived from `str.splitlines()`); tabs are allowed. For names rendered line by line, so a line break cannot forge an extra listing entry |
50
50
  | `SearchQueryStr64` | `2 <= len <= 64` after stripping: a one-character trigram query has no selective trigrams and degrades into a near-full table scan |
51
+ | `UUIDPrefix` | The beginning of a UUID in canonical text form, `4 <= len <= 36` after stripping: hex digits in either case, `-` only right after a complete `8-4-4-4-12` group (`"0192f4"`, `"0192f4a1-7c"`; `"zzzz"` and `"0192-f4"` are rejected). Used by [`TableViewRequest.id_prefix`](/en/reference/pagination-types) |
51
52
  | `HttpHeaderName` | RFC 9110 `token` (letters, digits and ``!#$%&'*+-.^_`|~``), `1 <= len <= 64` |
52
53
  | `Sha256Hex` | Exactly 64 lowercase hex characters (a SHA-256 digest) |
53
54
  | `BCP47LanguageCode` | BCP-47 language tag syntax (e.g. `zh-Hans-CN`), `max_length=16` |
@@ -10,7 +10,7 @@ Class hierarchy:
10
10
  PageWindowRequest offset / limit / desc
11
11
  └── PaginationRequest + order / after_id (keyset cursor)
12
12
  TimeFilterRequest created_* / updated_* time bounds
13
- TableViewRequest TimeFilterRequest + PaginationRequest
13
+ TableViewRequest TimeFilterRequest + PaginationRequest + id_prefix
14
14
  ```
15
15
 
16
16
  They are all pure data classes that only carry parameters; the SQL clauses are built by `TableBaseMixin.get()`.
@@ -84,10 +84,16 @@ from sqlmodel_ext import TableViewRequest
84
84
 
85
85
  ```python
86
86
  class TableViewRequest(TimeFilterRequest, PaginationRequest):
87
- pass
87
+ id_prefix: UUIDPrefix | None = None
88
88
  ```
89
89
 
90
- Carries pagination + ordering + keyset cursor + time filter parameters together. `get()` / `get_with_count()` accept a `table_view` parameter; explicitly passed `offset` / `limit` / `order_by` / time parameters take precedence, falling back to `table_view` when not provided.
90
+ Carries pagination + ordering + keyset cursor + time filter + ID prefix parameters together. `get()` / `get_with_count()` accept a `table_view` parameter; explicitly passed `offset` / `limit` / `order_by` / time parameters take precedence, falling back to `table_view` when not provided.
91
+
92
+ | Field | Type | Default | Semantics |
93
+ |------|------|--------|------|
94
+ | `id_prefix` | [`UUIDPrefix`](/en/reference/field-types) `\| None` | `None` | Only rows whose UUID primary key, in canonical text form, starts with this prefix; case-insensitive, surrounding whitespace removed |
95
+
96
+ `get()` / `get_with_count()` apply it as a primary-key range (`id BETWEEN lower AND upper`), so it can use the primary-key index instead of scanning the table; the count of `get_with_count()` follows the same filter. To filter a plain `count()`, pass `count(id_prefix=...)` — `time_filter` does not carry it. Like the time filters, it narrows the page but is not part of the keyset cursor's anchor visibility. A model whose primary key is not a UUID raises `ValueError` when it is given.
91
97
 
92
98
  ## `query_dependency()`
93
99
 
@@ -150,7 +150,7 @@ class User(SQLModel, table=True):
150
150
 
151
151
  ## 哨兵值:当 `None` 本身是一个有意义的值
152
152
 
153
- **哨兵值(sentinel)**是一个独一无二的对象,唯一的作用是表示"某种特殊情况",用 `is` 比较。Python 里早就有这种用法(`dataclasses.MISSING`、私有的 `_NOTSET = object()` 常量),[PEP 661](https://peps.python.org/pep-0661/) 把它标准化了。Pydantic 2.12 提供了一个:`pydantic.experimental.missing_sentinel.MISSING`,sqlmodel-ext 以 `Unset` 的名字导出它。
153
+ **哨兵值(sentinel)**是一个独一无二的对象,唯一的作用是表示"某种特殊情况",用 `is` 比较。Python 里早就有这种用法(`dataclasses.MISSING`、私有的 `_NOTSET = object()` 常量),[PEP 661](https://peps.python.org/pep-0661/) 把它标准化了。Pydantic 提供了一个:`MISSING`(2.12 起为 `pydantic.experimental.missing_sentinel.MISSING`,2.14 起为 `pydantic.MISSING`),sqlmodel-ext 以 `Unset` 的名字导出它。
154
154
 
155
155
  为什么需要它:在 PATCH 请求体里,`None` 必须能表示"清空这一列",于是"这个字段没传"就需要另一个载体。有了 `Unset`,三种状态——没传 / `null` / 有值——就能区分开:
156
156
 
@@ -38,7 +38,7 @@ class ArticleUpdate(SQLModelBase):
38
38
  from sqlmodel_ext import Unset
39
39
  ```
40
40
 
41
- `Unset` 就是 Pydantic 官方的 `pydantic.experimental.missing_sentinel.MISSING`(PEP 661 风格的哨兵),sqlmodel-ext 只是给它换了个名字导出。「没传」由 `Unset` 承载之后,`None` 重新变回一个**普通的值**。
41
+ `Unset` 就是 Pydantic 官方的 `MISSING` 哨兵(PEP 661 风格;Pydantic 2.14 起为 `pydantic.MISSING`,2.12–2.13 为 `pydantic.experimental.missing_sentinel.MISSING`),sqlmodel-ext 只是给它换了个名字导出。「没传」由 `Unset` 承载之后,`None` 重新变回一个**普通的值**。
42
42
 
43
43
  | 注解 | 含义 |
44
44
  |---|---|
@@ -94,7 +94,7 @@ else:
94
94
  - **传了值** → 照常校验(长度、范围等约束全部保留)。
95
95
 
96
96
  ::: warning 错误列表的顺序不要依赖
97
- `Unset | T` 是一个联合类型,校验失败时 `ValidationError.errors()` 里会有每个分支各自的错误(例如上面 `title=null` 会同时出现 `missing_sentinel_error` 与 `string_type`,`loc` 里带有分支标签如 `'missing-sentinel'` / `'constrained-str'`)。成员的顺序不影响**接受/拒绝哪些输入**,但会影响错误条目的**顺序**;下游不要依赖这个顺序。
97
+ `Unset | T` 是一个联合类型。Pydantic 2.12–2.13 下校验失败时,`ValidationError.errors()` 里会有每个分支各自的错误(例如上面 `title=null` 会同时出现 `missing_sentinel_error` 与 `string_type`,`loc` 里带有分支标签如 `'missing-sentinel'` / `'constrained-str'`);Pydantic 2.14 起不再报告 `MISSING` 分支,只剩 `T` 分支的那条错误(`string_type`,`loc` 到字段名为止)。成员的顺序不影响**接受/拒绝哪些输入**,但会影响错误条目的**顺序**;下游不要依赖这个顺序。
98
98
  :::
99
99
 
100
100
  ## `partial=True`:从基类派生 PATCH DTO
@@ -312,6 +312,6 @@ assert Event(marker=True).model_dump() == {'marker': True}
312
312
 
313
313
  ## 已知限制
314
314
 
315
- - Pydantic 仍把该特性标为 experimental(模块路径 `pydantic.experimental.missing_sentinel`,自 Pydantic 2.12 起提供),因此 sqlmodel-ext 0.5 要求 `pydantic>=2.12`。
315
+ - 该哨兵自 Pydantic 2.12 起以 experimental 形式提供(`pydantic.experimental.missing_sentinel.MISSING`),因此 sqlmodel-ext 0.5 要求 `pydantic>=2.12`。Pydantic 2.14 将其转正为 `pydantic.MISSING`,并弃用 experimental 路径;sqlmodel-ext 从 `pydantic_core` 导入它,在所有支持的版本上都不会触发弃用警告。
316
316
  - 静态收窄需要 PEP 661 支持(见上文版本要求);partial 派生字段在单独运行 basedpyright 时不参与静态收窄,需显式声明或用 [`check_derived`](/how-to/check-partial-dtos)(见上文)。
317
317
  - 开启 `omitted_sentinel` 的模型,字符串字段不能持有字面量 `'__omitted__'`。
@@ -120,7 +120,7 @@ class ToolArguments(SQLModelBase):
120
120
  from sqlmodel_ext import Unset, OMITTED_SENTINEL
121
121
  ```
122
122
 
123
- - `Unset`:Pydantic 官方的 `pydantic.experimental.missing_sentinel.MISSING`,表示"这个字段没有提供"。用 `x is Unset` 判断。值为 `Unset` 的字段不出现在 `model_dump()` / `model_dump_json()` 输出中。`copy.deepcopy(Unset) is Unset`。
123
+ - `Unset`:Pydantic 官方的 `MISSING` 哨兵(Pydantic 2.14 起为 `pydantic.MISSING`,此前为 `pydantic.experimental.missing_sentinel.MISSING`),表示"这个字段没有提供"。用 `x is Unset` 判断。值为 `Unset` 的字段不出现在 `model_dump()` / `model_dump_json()` 输出中。`copy.deepcopy(Unset) is Unset`。
124
124
  - `OMITTED_SENTINEL`:`'__omitted__'`,线上哨兵值,只存在于开启 `omitted_sentinel` 的模型的 JSON Schema 与入站负载中,进门即被归一为 `Unset`。业务代码不应与它比较。
125
125
 
126
126
  完整语义见 [Unset 三态](/explanation/unset-three-state)。
@@ -280,10 +280,11 @@ async def count(
280
280
  created_after_datetime: datetime | None = None,
281
281
  updated_before_datetime: datetime | None = None,
282
282
  updated_after_datetime: datetime | None = None,
283
+ id_prefix: UUIDPrefix | None = None,
283
284
  ) -> int
284
285
  ```
285
286
 
286
- 返回符合条件的记录数,底层 `SELECT COUNT(*)`。传 `distinct_column` 时改为 `COUNT(DISTINCT col)`(如"活跃用户去重数")。`time_filter` 中的非空字段优先于单独传入的时间参数。
287
+ 返回符合条件的记录数,底层 `SELECT COUNT(*)`。传 `distinct_column` 时改为 `COUNT(DISTINCT col)`(如"活跃用户去重数")。`time_filter` 中的非空字段优先于单独传入的时间参数。`id_prefix` 只统计 UUID 主键以该前缀开头的行(与 [`TableViewRequest.id_prefix`](/reference/pagination-types) 同一个过滤);`time_filter` 不携带它,即使传进去的是 `TableViewRequest`。
287
288
 
288
289
  ## `distinct_column()`
289
290
 
@@ -37,7 +37,7 @@ from sqlmodel_ext import Str1, Str16, Str24, Str32, Str36, Str48, Str64, Str100,
37
37
  from sqlmodel_ext import (
38
38
  NonEmptyStr64, NonEmptyStr128, NonEmptyStr256,
39
39
  NonEmptyStrippedStr32, NonEmptyStrippedStr64, NonEmptyStrippedStr128, NonEmptyStrippedStr256,
40
- SingleLineStr64, SearchQueryStr64, HttpHeaderName,
40
+ SingleLineStr64, SearchQueryStr64, UUIDPrefix, HttpHeaderName,
41
41
  Sha256Hex, BCP47LanguageCode,
42
42
  )
43
43
  ```
@@ -48,6 +48,7 @@ from sqlmodel_ext import (
48
48
  | `NonEmptyStrippedStr32/64/128/256` | 同上 + `strip_whitespace`,拒绝纯空白(`" "` / `"\t"`) |
49
49
  | `SingleLineStr64` | 等于 `NonEmptyStrippedStr64` + **单行**:拒绝 NUL 与所有换行字符(`\n \v \f \r \x1c \x1d \x1e \x85 
 
`,由 `str.splitlines()` 推导);制表符允许。用于逐行渲染的名称,防止用换行伪造列表条目 |
50
50
  | `SearchQueryStr64` | 去空白后 `2 <= len <= 64`:单字符 trigram 查询没有选择性,会退化成近乎全表扫描 |
51
+ | `UUIDPrefix` | UUID 标准文本形式的开头部分,去空白后 `4 <= len <= 36`:十六进制字符不区分大小写,`-` 只能紧跟在完整的 `8-4-4-4-12` 分组之后(`"0192f4"`、`"0192f4a1-7c"`;`"zzzz"`、`"0192-f4"` 会被拒绝)。供 [`TableViewRequest.id_prefix`](/reference/pagination-types) 使用 |
51
52
  | `HttpHeaderName` | RFC 9110 `token`(字母、数字与 ``!#$%&'*+-.^_`|~``),`1 <= len <= 64` |
52
53
  | `Sha256Hex` | 精确 64 位小写 hex(SHA-256 摘要) |
53
54
  | `BCP47LanguageCode` | BCP-47 语言代码语法(如 `zh-Hans-CN`),`max_length=16` |
@@ -10,7 +10,7 @@
10
10
  PageWindowRequest offset / limit / desc
11
11
  └── PaginationRequest + order / after_id(keyset 游标)
12
12
  TimeFilterRequest created_* / updated_* 时间边界
13
- TableViewRequest TimeFilterRequest + PaginationRequest
13
+ TableViewRequest TimeFilterRequest + PaginationRequest + id_prefix
14
14
  ```
15
15
 
16
16
  它们都是纯数据类,只承载参数;SQL 子句由 `TableBaseMixin.get()` 构造。
@@ -84,10 +84,16 @@ from sqlmodel_ext import TableViewRequest
84
84
 
85
85
  ```python
86
86
  class TableViewRequest(TimeFilterRequest, PaginationRequest):
87
- pass
87
+ id_prefix: UUIDPrefix | None = None
88
88
  ```
89
89
 
90
- 同时承载分页 + 排序 + keyset 游标 + 时间过滤参数。`get()` / `get_with_count()` 接受 `table_view` 参数;显式传入的 `offset` / `limit` / `order_by` / 时间参数优先,未提供时回退到 `table_view`。
90
+ 同时承载分页 + 排序 + keyset 游标 + 时间过滤 + ID 前缀参数。`get()` / `get_with_count()` 接受 `table_view` 参数;显式传入的 `offset` / `limit` / `order_by` / 时间参数优先,未提供时回退到 `table_view`。
91
+
92
+ | 字段 | 类型 | 默认值 | 语义 |
93
+ |------|------|--------|------|
94
+ | `id_prefix` | [`UUIDPrefix`](/reference/field-types) `\| None` | `None` | 只返回 UUID 主键(标准文本形式)以该前缀开头的行;不区分大小写,首尾空白会去除 |
95
+
96
+ `get()` / `get_with_count()` 把它转成主键范围(`id BETWEEN 下界 AND 上界`),可以走主键索引而不是全表扫描;`get_with_count()` 的总数按同一条件统计。单独调用 `count()` 时传 `count(id_prefix=...)`——`time_filter` 不携带它。和时间过滤一样,它只收窄结果,不参与 keyset 游标的锚点可见性判断。主键不是 UUID 的模型传了它会抛 `ValueError`。
91
97
 
92
98
  ## `query_dependency()`
93
99
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sqlmodel-ext"
7
- version = "0.5.4"
7
+ version = "0.5.5"
8
8
  description = "Extended SQLModel infrastructure: smart metaclass, async CRUD mixins, polymorphic inheritance, optimistic locking, relation preloading, and reusable field types."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -31,8 +31,9 @@ dependencies = [
31
31
  # 0.0.48 made ``Select`` variadic (``Select[A, B]``); ``sqlmodel_ext.select``'s
32
32
  # overloads mirror that spelling, which earlier releases reject.
33
33
  "sqlmodel>=0.0.48",
34
- # 2.12 is the first release shipping ``pydantic.experimental.missing_sentinel.MISSING``
35
- # (the object ``Unset`` is) and ``Field(exclude_if=...)`` (behind ``EXCLUDE_IF_NONE``).
34
+ # 2.12 is the first release shipping the ``MISSING`` sentinel (the object ``Unset`` is;
35
+ # ``pydantic.MISSING`` since 2.14, ``pydantic.experimental.missing_sentinel`` before) and
36
+ # ``Field(exclude_if=...)`` (behind ``EXCLUDE_IF_NONE``).
36
37
  "pydantic>=2.12",
37
38
  # The ``asyncio`` extra pulls in ``greenlet``: ``sqlmodel_ext.session`` imports
38
39
  # ``sqlalchemy.ext.asyncio`` at module level, and SQLAlchemy 2.1 made greenlet optional.