fastapi-rtk 2.12.3__tar.gz → 2.13.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. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/PKG-INFO +1 -1
  2. fastapi_rtk-2.13.0/fastapi_rtk/_version.py +1 -0
  3. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/generic/column.py +87 -18
  4. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/generic/model.py +18 -5
  5. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/__init__.py +0 -1
  6. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/interface.py +3 -68
  7. fastapi_rtk-2.13.0/fastapi_rtk/exceptions.py +235 -0
  8. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/globals.py +21 -2
  9. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/conftest.py +67 -0
  10. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/generic/test_column.py +94 -0
  11. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/generic/test_model.py +79 -4
  12. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/bases/test_interface.py +76 -5
  13. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_exceptions.py +30 -0
  14. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_globals.py +38 -0
  15. fastapi_rtk-2.12.3/fastapi_rtk/_version.py +0 -1
  16. fastapi_rtk-2.12.3/fastapi_rtk/exceptions.py +0 -80
  17. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/.gitignore +0 -0
  18. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/LICENSE +0 -0
  19. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/README.md +0 -0
  20. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/__init__.py +0 -0
  21. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/__init__.py +0 -0
  22. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/base_api.py +0 -0
  23. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/__init__.py +0 -0
  24. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_add_mixin.py +0 -0
  25. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_bulk_mixin.py +0 -0
  26. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_columns_mixin.py +0 -0
  27. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_delete_mixin.py +0 -0
  28. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_download_mixin.py +0 -0
  29. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_edit_mixin.py +0 -0
  30. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_file_mixin.py +0 -0
  31. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_info_mixin.py +0 -0
  32. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_list_mixin.py +0 -0
  33. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_params.py +0 -0
  34. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_show_mixin.py +0 -0
  35. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_typed_base.py +0 -0
  36. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/api/model_rest_api/_upload_mixin.py +0 -0
  37. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/apis/__init__.py +0 -0
  38. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/apis/info.py +0 -0
  39. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/apis/license.py +0 -0
  40. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/__init__.py +0 -0
  41. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/auth.py +0 -0
  42. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/hashers/__init__.py +0 -0
  43. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
  44. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
  45. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/ldap.py +0 -0
  46. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
  47. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
  48. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/strategies/__init__.py +0 -0
  49. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/strategies/config.py +0 -0
  50. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/strategies/db.py +0 -0
  51. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/auth/strategies/jwt.py +0 -0
  52. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/__init__.py +0 -0
  53. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/generic/__init__.py +0 -0
  54. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/generic/db.py +0 -0
  55. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/generic/exceptions.py +0 -0
  56. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/generic/filters.py +0 -0
  57. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/generic/interface.py +0 -0
  58. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/generic/session.py +0 -0
  59. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/__init__.py +0 -0
  60. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/column.py +0 -0
  61. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/db.py +0 -0
  62. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
  63. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
  64. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
  65. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_mixin.py +0 -0
  66. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_mixin.py +0 -0
  67. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
  68. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/audit/_processing_mixin.py +0 -0
  69. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
  70. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
  71. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
  72. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
  73. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
  74. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/filters.py +0 -0
  75. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/interface.py +0 -0
  76. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/model.py +0 -0
  77. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/backends/sqla/session.py +0 -0
  78. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/cache.py +0 -0
  79. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/db.py +0 -0
  80. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/file_manager.py +0 -0
  81. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/filter.py +0 -0
  82. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/loop_manager.py +0 -0
  83. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/model.py +0 -0
  84. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/session.py +0 -0
  85. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/bases/user_cache.py +0 -0
  86. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/caches/__init__.py +0 -0
  87. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/caches/cache.py +0 -0
  88. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/caches/serializers.py +0 -0
  89. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/caches/storages.py +0 -0
  90. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/__init__.py +0 -0
  91. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/cli.py +0 -0
  92. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/__init__.py +0 -0
  93. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
  94. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
  95. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
  96. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
  97. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
  98. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
  99. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
  100. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
  101. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
  102. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
  103. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/export.py +0 -0
  104. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/security.py +0 -0
  105. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/commands/translate.py +0 -0
  106. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/const.py +0 -0
  107. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/decorators.py +0 -0
  108. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/types.py +0 -0
  109. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/cli/utils.py +0 -0
  110. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/config.py +0 -0
  111. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/const.py +0 -0
  112. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/db.py +0 -0
  113. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/decorators.py +0 -0
  114. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/dependencies.py +0 -0
  115. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
  116. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/file_managers/__init__.py +0 -0
  117. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/file_managers/file_manager.py +0 -0
  118. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/file_managers/image_manager.py +0 -0
  119. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
  120. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
  121. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/filters.py +0 -0
  122. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/__init__.py +0 -0
  123. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/babel/__init__.py +0 -0
  124. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/babel/cli.py +0 -0
  125. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/babel/config.py +0 -0
  126. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/babel.cfg +0 -0
  127. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/lazy_text.py +0 -0
  128. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/messages.pot +0 -0
  129. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  130. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
  131. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  132. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
  133. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/loop_managers/__init__.py +0 -0
  134. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/loop_managers/loop_manager.py +0 -0
  135. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/manager.py +0 -0
  136. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/middlewares.py +0 -0
  137. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/mixins.py +0 -0
  138. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/models.py +0 -0
  139. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/py.typed +0 -0
  140. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/registry.py +0 -0
  141. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/routers.py +0 -0
  142. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/schemas.py +0 -0
  143. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/__init__.py +0 -0
  144. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/__init__.py +0 -0
  145. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/apis.py +0 -0
  146. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/models.py +0 -0
  147. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
  148. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/security_manager/_association_mixin.py +0 -0
  149. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/security_manager/_builtin_role_mixin.py +0 -0
  150. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/security_manager/_cleanup_mixin.py +0 -0
  151. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
  152. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
  153. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
  154. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/security_manager/_role_mixin.py +0 -0
  155. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/security_manager/_user_mixin.py +0 -0
  156. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/security/sqla/user_cache.py +0 -0
  157. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/setting.py +0 -0
  158. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/types.py +0 -0
  159. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/__init__.py +0 -0
  160. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/async_task_runner.py +0 -0
  161. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/class_factory.py +0 -0
  162. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/csv_json_converter.py +0 -0
  163. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/deep_merge.py +0 -0
  164. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/extender_mixin.py +0 -0
  165. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
  166. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/formatter.py +0 -0
  167. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/hooks.py +0 -0
  168. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/lazy.py +0 -0
  169. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/merge_schema.py +0 -0
  170. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
  171. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/pydantic.py +0 -0
  172. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/run_utils.py +0 -0
  173. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/self_dependencies.py +0 -0
  174. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
  175. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/sqla.py +0 -0
  176. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/timezone.py +0 -0
  177. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/update_signature.py +0 -0
  178. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/utils/use_default_when_none.py +0 -0
  179. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/fastapi_rtk/version.py +0 -0
  180. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/pyproject.toml +0 -0
  181. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/docker-compose.gis.yml +0 -0
  182. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/__init__.py +0 -0
  183. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/api/__init__.py +0 -0
  184. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/api/test_model_rest_api.py +0 -0
  185. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/apis/__init__.py +0 -0
  186. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/apis/test_info_callable_schema.py +0 -0
  187. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/apis/test_info_contract.py +0 -0
  188. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/auth/__init__.py +0 -0
  189. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/auth/test_auth_flow.py +0 -0
  190. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/auth/test_oauth.py +0 -0
  191. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/conftest.py +0 -0
  192. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/file_managers/__init__.py +0 -0
  193. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/file_managers/test_file_routes.py +0 -0
  194. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/security/__init__.py +0 -0
  195. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/security/sqla/__init__.py +0 -0
  196. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/security/sqla/test_security_manager.py +0 -0
  197. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/integration/test_app_smoke.py +0 -0
  198. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/__init__.py +0 -0
  199. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/api/__init__.py +0 -0
  200. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/api/conftest.py +0 -0
  201. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/api/model_rest_api/__init__.py +0 -0
  202. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/api/model_rest_api/test_columns.py +0 -0
  203. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
  204. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/api/model_rest_api/test_init.py +0 -0
  205. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/api/model_rest_api/test_typed_base_consistency.py +0 -0
  206. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/api/test_base_api.py +0 -0
  207. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/apis/__init__.py +0 -0
  208. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/apis/test_info.py +0 -0
  209. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/apis/test_license.py +0 -0
  210. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/__init__.py +0 -0
  211. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/hashers/__init__.py +0 -0
  212. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
  213. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/hashers/test_scrypt.py +0 -0
  214. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/password_helpers/__init__.py +0 -0
  215. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/password_helpers/test_fab.py +0 -0
  216. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/strategies/__init__.py +0 -0
  217. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/strategies/test_config.py +0 -0
  218. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/strategies/test_db.py +0 -0
  219. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/strategies/test_jwt.py +0 -0
  220. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/auth/test_auth.py +0 -0
  221. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/__init__.py +0 -0
  222. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/generic/__init__.py +0 -0
  223. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/generic/test_db.py +0 -0
  224. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/generic/test_exceptions.py +0 -0
  225. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/generic/test_filters.py +0 -0
  226. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/generic/test_interface.py +0 -0
  227. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/generic/test_session.py +0 -0
  228. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/__init__.py +0 -0
  229. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/conftest.py +0 -0
  230. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/test_audit.py +0 -0
  231. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/test_audit_types.py +0 -0
  232. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/test_column.py +0 -0
  233. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/test_db.py +0 -0
  234. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/test_filters.py +0 -0
  235. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
  236. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/test_interface.py +0 -0
  237. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/test_model.py +0 -0
  238. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/backends/sqla/test_session.py +0 -0
  239. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/bases/__init__.py +0 -0
  240. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/bases/test_db.py +0 -0
  241. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/bases/test_file_manager.py +0 -0
  242. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/bases/test_filter.py +0 -0
  243. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/bases/test_model.py +0 -0
  244. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/bases/test_session.py +0 -0
  245. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/__init__.py +0 -0
  246. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/commands/__init__.py +0 -0
  247. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/commands/conftest.py +0 -0
  248. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/commands/test_db.py +0 -0
  249. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/commands/test_export.py +0 -0
  250. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/commands/test_security.py +0 -0
  251. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/commands/test_security_crypto.py +0 -0
  252. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/commands/test_translate.py +0 -0
  253. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/conftest.py +0 -0
  254. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/test_cli.py +0 -0
  255. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/test_decorators.py +0 -0
  256. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/test_types.py +0 -0
  257. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/cli/test_utils.py +0 -0
  258. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/file_managers/__init__.py +0 -0
  259. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/file_managers/conftest.py +0 -0
  260. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/file_managers/test_file_manager.py +0 -0
  261. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/file_managers/test_image_manager.py +0 -0
  262. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
  263. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
  264. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/lang/__init__.py +0 -0
  265. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/lang/test_babel_cli.py +0 -0
  266. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/lang/test_babel_config.py +0 -0
  267. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/lang/test_lazy_text.py +0 -0
  268. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/security/__init__.py +0 -0
  269. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/security/sqla/__init__.py +0 -0
  270. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/security/sqla/conftest.py +0 -0
  271. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/security/sqla/test_apis.py +0 -0
  272. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/security/sqla/test_models.py +0 -0
  273. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/security/sqla/test_security_manager.py +0 -0
  274. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_caches.py +0 -0
  275. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_config.py +0 -0
  276. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_const.py +0 -0
  277. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_db.py +0 -0
  278. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_decorators.py +0 -0
  279. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_dependencies.py +0 -0
  280. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_fastapi_react_toolkit.py +0 -0
  281. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_filters.py +0 -0
  282. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_manager.py +0 -0
  283. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_middlewares.py +0 -0
  284. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_mixins.py +0 -0
  285. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_models.py +0 -0
  286. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_registry.py +0 -0
  287. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_routers.py +0 -0
  288. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_schemas.py +0 -0
  289. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_setting.py +0 -0
  290. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_types.py +0 -0
  291. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_user_cache.py +0 -0
  292. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/test_version.py +0 -0
  293. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/__init__.py +0 -0
  294. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_async_task_runner.py +0 -0
  295. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_class_factory.py +0 -0
  296. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_csv_json_converter.py +0 -0
  297. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_deep_merge.py +0 -0
  298. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_extender_mixin.py +0 -0
  299. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
  300. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_formatter.py +0 -0
  301. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_hooks.py +0 -0
  302. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_lazy.py +0 -0
  303. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_merge_schema.py +0 -0
  304. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
  305. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_pydantic.py +0 -0
  306. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_run_utils.py +0 -0
  307. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_self_dependencies.py +0 -0
  308. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_smartdefaultdict.py +0 -0
  309. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_sqla.py +0 -0
  310. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_timezone.py +0 -0
  311. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_update_signature.py +0 -0
  312. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/tests/unit/utils/test_use_default_when_none.py +0 -0
  313. {fastapi_rtk-2.12.3 → fastapi_rtk-2.13.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fastapi-rtk
3
- Version: 2.12.3
3
+ Version: 2.13.0
4
4
  Summary: A package that provides a set of tools to build a FastAPI application with a Class-Based CRUD API.
5
5
  Project-URL: Homepage, https://codeberg.org/datatactics/fastapi-rtk
6
6
  Project-URL: Issues, https://codeberg.org/datatactics/fastapi-rtk/issues
@@ -0,0 +1 @@
1
+ __version__ = "2.13.0"
@@ -1,4 +1,5 @@
1
1
  import collections.abc
2
+ import copy
2
3
  import inspect
3
4
  import typing
4
5
 
@@ -6,7 +7,11 @@ import pydantic
6
7
 
7
8
  from ...mixins import LoggerMixin
8
9
  from ...utils import T
9
- from .exceptions import ColumnInvalidTypeException, GenericColumnException
10
+ from .exceptions import (
11
+ ColumnInvalidTypeException,
12
+ ColumnNotSetException,
13
+ GenericColumnException,
14
+ )
10
15
 
11
16
  if typing.TYPE_CHECKING:
12
17
  from .model import GenericModel
@@ -45,7 +50,7 @@ class GenericColumn(typing.Generic[T], LoggerMixin):
45
50
  auto_increment (bool, optional): Whether the column is auto incremented. Only works if the column is a primary key. Defaults to False.
46
51
  unique (bool, optional): Whether the column is unique. Defaults to False.
47
52
  nullable (bool, optional): Whether the column is nullable. Defaults to True.
48
- default (T | collections.abc.Callable[[], T] | collections.abc.Callable[[GenericModel], T] | NoDefault, optional): The default value of the column. Will be used if the column is not set by the user. Defaults to NoDefault (no default value).
53
+ default (T | collections.abc.Callable[[], T] | collections.abc.Callable[[GenericModel], T] | NoDefault, optional): The default value of the column. Will be used if the column is not set by the user. It is resolved once, when the model is initialized: a callable taking no argument is called, and a mutable value is copied. Defaults to NoDefault (no default value).
49
54
  """
50
55
 
51
56
  col_type: type[T]
@@ -78,7 +83,7 @@ class GenericColumn(typing.Generic[T], LoggerMixin):
78
83
  auto_increment (bool, optional): Whether the column is auto incremented. Only works if the column is a primary key. Defaults to False.
79
84
  unique (bool, optional): Whether the column is unique. Defaults to False.
80
85
  nullable (bool, optional): Whether the column is nullable. Defaults to True.
81
- default (T | collections.abc.Callable[[], T] | collections.abc.Callable[["GenericModel"], T] | _NoDefaultValue, optional): The default value of the column. Will be used if the column is not set by the user. Defaults to UNSET (no default value).
86
+ default (T | collections.abc.Callable[[], T] | collections.abc.Callable[["GenericModel"], T] | _NoDefaultValue, optional): The default value of the column. Will be used if the column is not set by the user. It is resolved once, when the model is initialized: a callable taking no argument is called, and a mutable value is copied. Defaults to UNSET (no default value).
82
87
 
83
88
  Raises:
84
89
  GenericColumnException: If auto increment is set on a non-primary key column or if the column type is not int when auto increment is set.
@@ -95,10 +100,8 @@ class GenericColumn(typing.Generic[T], LoggerMixin):
95
100
  self.unique = True
96
101
  self.nullable = False
97
102
 
98
- if inspect.isfunction(self.default):
99
- # If the function takes no parameters, convert it to a function that needs the instance
100
- if inspect.signature(self.default).parameters == {}:
101
- self.default = lambda instance, func=self.default: func()
103
+ if self.default is not UNSET:
104
+ self.default = self._normalize_default(self.default)
102
105
 
103
106
  if not self.primary_key and self.auto_increment:
104
107
  raise GenericColumnException(
@@ -125,22 +128,30 @@ class GenericColumn(typing.Generic[T], LoggerMixin):
125
128
  instance: "GenericModel | None",
126
129
  owner: type["GenericModel"],
127
130
  ):
131
+ """
132
+ Read the column value of the instance, or the descriptor itself when read on the class.
133
+
134
+ Args:
135
+ instance (GenericModel | None): The instance to read from, or None when read on the class.
136
+ owner (type[GenericModel]): The model class owning the column.
137
+
138
+ Raises:
139
+ ColumnNotSetException: If a column that is neither nullable nor an auto incrementing primary key was never set.
140
+
141
+ Returns:
142
+ GenericColumn | T | None: The descriptor on class access, else the value, None for an unset nullable column, or PK_AUTO_INCREMENT for an unset auto incrementing primary key.
143
+ """
128
144
  if not instance:
129
145
  return self
130
146
 
131
- default_value = self.default
132
- if callable(default_value):
133
- default_value = default_value(instance)
134
-
135
- value: T | GenericUnset = instance.__dict__.get(
136
- f"_{self._col_name}", default_value
137
- )
147
+ value: T | GenericUnset = instance.__dict__.get(f"_{self._col_name}", UNSET)
138
148
 
139
- if value is default_value:
140
- if self.nullable and value is UNSET:
149
+ if value is UNSET:
150
+ if self.nullable:
141
151
  return None
142
- elif self.primary_key and self.auto_increment:
152
+ if self.primary_key and self.auto_increment:
143
153
  return PK_AUTO_INCREMENT
154
+ raise ColumnNotSetException(instance, f"Column {self._col_name} is not set")
144
155
 
145
156
  return value
146
157
 
@@ -161,7 +172,8 @@ class GenericColumn(typing.Generic[T], LoggerMixin):
161
172
  if self.nullable:
162
173
  validation_type = validation_type | None
163
174
  if self.default is not UNSET:
164
- validation_type = validation_type | type(self.default)
175
+ if not callable(self.default):
176
+ validation_type = validation_type | type(self.default)
165
177
  validation_field = pydantic.Field(default=self.default)
166
178
  else:
167
179
  validation_field = pydantic.Field
@@ -194,3 +206,60 @@ class GenericColumn(typing.Generic[T], LoggerMixin):
194
206
  f"_{self._col_name}" in instance.__dict__
195
207
  and getattr(instance, f"_{self._col_name}") is not UNSET
196
208
  )
209
+
210
+ def apply_default(self, instance: "GenericModel") -> None:
211
+ """
212
+ Write the column default into the instance, leaving an already set column untouched.
213
+
214
+ A default resolving to a sentinel is skipped, so a default reading a not yet assigned primary key leaves the column unset.
215
+
216
+ Args:
217
+ instance (GenericModel): The instance to write the default into.
218
+ """
219
+ if self.default is UNSET or self.is_column_set(instance):
220
+ return
221
+
222
+ value = self.default(instance) if callable(self.default) else self.default
223
+ if isinstance(value, (GenericUnset, GenericPKAutoIncrement)):
224
+ return
225
+
226
+ instance.__dict__[f"_{self._col_name}"] = value
227
+
228
+ def _normalize_default(
229
+ self,
230
+ default: T | collections.abc.Callable[..., T],
231
+ ) -> T | collections.abc.Callable[["GenericModel"], T]:
232
+ """
233
+ Normalize a column default into either a plain value or a callable taking the model instance.
234
+
235
+ A mutable value becomes a copying callable, so one instance cannot mutate the default of every other instance.
236
+
237
+ Args:
238
+ default (T | collections.abc.Callable[..., T]): The default given to the column.
239
+
240
+ Returns:
241
+ T | collections.abc.Callable[[GenericModel], T]: The value itself, or a callable taking the instance.
242
+ """
243
+ if not callable(default):
244
+ if isinstance(default, collections.abc.Hashable):
245
+ return default
246
+ return lambda instance, value=default: copy.deepcopy(value)
247
+
248
+ try:
249
+ parameters = inspect.signature(default).parameters.values()
250
+ except (TypeError, ValueError):
251
+ # A builtin without an introspectable signature (dict, str, ...) can only be a zero argument factory.
252
+ return lambda instance, func=default: func()
253
+
254
+ if any(
255
+ parameter.default is inspect.Parameter.empty
256
+ and parameter.kind
257
+ in (
258
+ inspect.Parameter.POSITIONAL_ONLY,
259
+ inspect.Parameter.POSITIONAL_OR_KEYWORD,
260
+ )
261
+ for parameter in parameters
262
+ ):
263
+ return default
264
+
265
+ return lambda instance, func=default: func()
@@ -117,15 +117,20 @@ class GenericModel(BasicModel):
117
117
  """
118
118
  Initialize the model with the given keyword arguments.
119
119
 
120
+ A column without a given value takes its default, like a dataclass mapped SQLAlchemy model does.
121
+
120
122
  Args:
121
123
  __check_on_init__ (bool, optional): Whether to check the model's validity on initialization. Defaults to True.
122
124
  """
123
- if not kwargs:
124
- return
125
125
  for key, value in kwargs.items():
126
126
  if key not in self.__mapper__.properties.keys():
127
127
  continue
128
128
  setattr(self, key, value)
129
+
130
+ # After the given values, so a default reading another column sees it.
131
+ for column in self.__mapper__.properties.values():
132
+ column.apply_default(self)
133
+
129
134
  if __check_on_init__:
130
135
  self.is_model_valid()
131
136
 
@@ -166,8 +171,10 @@ class GenericModel(BasicModel):
166
171
  """
167
172
  errors: list[Exception] = []
168
173
  for key in self.__mapper__.properties.keys():
169
- if getattr(self, key) is UNSET:
170
- errors.append(ColumnNotSetException(self, f"Column {key} is not set"))
174
+ try:
175
+ getattr(self, key)
176
+ except ColumnNotSetException as e:
177
+ errors.append(e)
171
178
 
172
179
  if errors:
173
180
  # Rollback to last valid state
@@ -186,4 +193,10 @@ class GenericModel(BasicModel):
186
193
  return [cls.__mapper__.pk]
187
194
 
188
195
  def __repr__(self):
189
- return f"{self.__class__.__name__}({';'.join([f'{col}={getattr(self, col)}' for col in self.__mapper__.properties.keys()])})"
196
+ values = []
197
+ for col in self.__mapper__.properties.keys():
198
+ try:
199
+ values.append(f"{col}={getattr(self, col)}")
200
+ except ColumnNotSetException:
201
+ values.append(f"{col}={UNSET}")
202
+ return f"{self.__class__.__name__}({';'.join(values)})"
@@ -23,7 +23,6 @@ __all__ = [
23
23
  "PydanticGenerationSchema",
24
24
  "AbstractInterface",
25
25
  "AbstractLoopManager",
26
- "MissingGreenletError",
27
26
  "BasicModel",
28
27
  "AbstractSession",
29
28
  "AbstractUserSerializer",
@@ -11,7 +11,7 @@ import pydantic
11
11
  import sqlalchemy.exc
12
12
 
13
13
  from ..const import logger
14
- from ..exceptions import FastAPIReactToolkitException, raise_exception
14
+ from ..exceptions import MissingGreenletError, raise_exception
15
15
  from ..globals import g
16
16
  from ..schemas import PRIMARY_KEY, ColumnEnumInfo, ColumnInfo, ColumnRelationInfo
17
17
  from ..utils import AsyncTaskRunner, C, T, deep_merge, lazy_self, smart_run
@@ -32,7 +32,6 @@ if typing.TYPE_CHECKING:
32
32
  __all__ = [
33
33
  "PydanticGenerationSchema",
34
34
  "AbstractInterface",
35
- "MissingGreenletError",
36
35
  ]
37
36
 
38
37
 
@@ -56,70 +55,6 @@ class PydanticGenerationSchema(typing.TypedDict):
56
55
  name_: tuple[type[str], pydantic.Field] | None
57
56
 
58
57
 
59
- class MissingGreenletError(FastAPIReactToolkitException):
60
- """Raised when an ORM attribute access during pydantic schema validation
61
- triggers a SQLAlchemy lazy-load outside the asyncio greenlet.
62
-
63
- Covers both async-property columns (awaited explicitly by
64
- ``_populate_async_column``) and regular columns / relationships read by
65
- pydantic's ``from_attributes`` machinery.
66
-
67
- The most common causes are:
68
-
69
- - The ORM instance is detached from its session (commit/close happened
70
- before the schema was rendered).
71
- - The async session was expired between the query and the
72
- response-serialisation step.
73
- - A relationship the schema reads has not been eager-loaded. Prefer
74
- ``selectinload`` / ``joinedload`` on the query, call
75
- ``await instance.load("<col>")`` before returning the instance, or
76
- add the column to ``list_columns`` / ``show_columns`` (or the
77
- narrower ``list_select_columns`` / ``show_select_columns``, or
78
- ``extra_list_fetch_columns`` / ``extra_show_fetch_columns`` when
79
- the column should be fetched but not surfaced in the response) on
80
- the ``ModelRestApi`` subclass so the toolkit eager-loads it for
81
- you.
82
- """
83
-
84
- def __init__(self, *, model, col: str | None, original: Exception):
85
- self.model = model
86
- self.col = col
87
- self.original = original
88
- model_label = type(model).__name__
89
- if col is None:
90
- super().__init__(
91
- f"Failed to validate schema for '{model_label}': SQLAlchemy "
92
- f"raised MissingGreenlet while pydantic accessed an attribute. "
93
- f"Fix by eager-loading the missing relationship "
94
- f"(`selectinload` / `joinedload`), by calling "
95
- f'`await instance.load("<col>")` before the instance leaves '
96
- f"the async context, or by adding the column to "
97
- f"`list_columns` / `show_columns` (or the narrower "
98
- f"`list_select_columns` / `show_select_columns`, or "
99
- f"`extra_list_fetch_columns` / `extra_show_fetch_columns` "
100
- f"when the column should be fetched but not surfaced in the "
101
- f"response) on the ModelRestApi subclass. Original error: "
102
- f"{original!r}."
103
- )
104
- else:
105
- super().__init__(
106
- f"Failed to populate column '{col}' on '{model_label}': "
107
- f"SQLAlchemy raised MissingGreenlet. The ORM attempted I/O "
108
- f"outside the async greenlet. Fix by eager-loading the "
109
- f"underlying relationship "
110
- f"(e.g. add `selectinload({model_label}.{col})` or "
111
- f"`joinedload({model_label}.{col})` to the query), by "
112
- f'calling `await instance.load("{col}")` before the instance '
113
- f'leaves the async context, or by adding `"{col}"` to '
114
- f"`list_columns` / `show_columns` (or the narrower "
115
- f"`list_select_columns` / `show_select_columns`, or "
116
- f"`extra_list_fetch_columns` / `extra_show_fetch_columns` "
117
- f"when the column should be fetched but not surfaced in the "
118
- f"response) on the ModelRestApi subclass. Original error: "
119
- f"{original!r}."
120
- )
121
-
122
-
123
58
  class lazy(lazy_self["AbstractInterface", T]): ...
124
59
 
125
60
 
@@ -1081,7 +1016,7 @@ class AbstractInterface(abc.ABC, typing.Generic[T, C, CT]):
1081
1016
  try:
1082
1017
  value = await getattr(model, col)
1083
1018
  except sqlalchemy.exc.MissingGreenlet as e:
1084
- raise MissingGreenletError(
1019
+ raise MissingGreenletError.from_attribute_read(
1085
1020
  model=model,
1086
1021
  col=col,
1087
1022
  original=e,
@@ -1119,7 +1054,7 @@ class AbstractInterface(abc.ABC, typing.Generic[T, C, CT]):
1119
1054
  try:
1120
1055
  getattr(value, field_name)
1121
1056
  except sqlalchemy.exc.MissingGreenlet as e:
1122
- raise MissingGreenletError(
1057
+ raise MissingGreenletError.from_attribute_read(
1123
1058
  model=value,
1124
1059
  col=field_name,
1125
1060
  original=e,
@@ -0,0 +1,235 @@
1
+ import types
2
+ import typing
3
+
4
+ import fastapi
5
+ import pydantic
6
+ import sqlalchemy.exc
7
+
8
+ from .utils import T, prettify_dict
9
+
10
+ __all__ = [
11
+ "raise_exception",
12
+ "FastAPIReactToolkitException",
13
+ "RolesMismatchException",
14
+ "HTTPWithValidationException",
15
+ "MissingGreenletError",
16
+ ]
17
+
18
+
19
+ def raise_exception(message: str, return_type: type[T] | None = None) -> T:
20
+ """
21
+ Raise a FastAPIReactToolkitException with the given message.
22
+
23
+ Args:
24
+ message (str): The error message.
25
+ return_type (type[T] | None, optional): The expected return type. Defaults to None.
26
+
27
+ Raises:
28
+ FastAPIReactToolkitException: Always raised.
29
+
30
+ Returns:
31
+ T: The return value.
32
+ """
33
+ raise FastAPIReactToolkitException(message)
34
+
35
+
36
+ class FastAPIReactToolkitException(Exception):
37
+ """Base exception for FastAPI React Toolkit errors."""
38
+
39
+
40
+ class RolesMismatchException(FastAPIReactToolkitException):
41
+ """Exception raised when the roles do not match the expected roles."""
42
+
43
+
44
+ class HTTPWithValidationException(fastapi.HTTPException):
45
+ """
46
+ Custom HTTP Exception for FastAPI with validation details.
47
+ """
48
+
49
+ URL = f"https://errors.pydantic.dev/{pydantic.version.version_short()}/v"
50
+ _no_input = object()
51
+
52
+ def __init__(
53
+ self,
54
+ status_code: int,
55
+ type: str,
56
+ location_type: typing.Literal["path", "query", "body"],
57
+ field: str,
58
+ msg: str,
59
+ input: typing.Any = _no_input,
60
+ headers: dict[str, str] | None = None,
61
+ ):
62
+ """
63
+ Initialize the HTTPWithValidationException.
64
+
65
+ Args:
66
+ status_code (int): HTTP status code for the exception.
67
+ type (str): Type of the error (e.g., "string_type", "validation_error").
68
+ location_type (typing.Literal["path", "query", "body"]): Location type of the error, either "path", "query", or "body".
69
+ field (str): Field name where the error occurred.
70
+ msg (str): Error message describing the validation issue.
71
+ input (typing.Any, optional): Optional input value that caused the error. Defaults to _no_input.
72
+ headers (dict[str, str] | None, optional): Optional headers to include in the response. Defaults to None.
73
+ """
74
+ obj = {"type": type, "loc": [location_type, field], "msg": msg}
75
+ if input is not self._no_input:
76
+ if isinstance(input, pydantic.BaseModel):
77
+ input = input.model_dump(mode="json")
78
+ else:
79
+ input = str(input)
80
+ obj["input"] = input
81
+ obj["url"] = f"{self.URL}/{type}"
82
+
83
+ super().__init__(status_code, [obj], headers)
84
+
85
+
86
+ class MissingGreenletError(FastAPIReactToolkitException):
87
+ """Raised when an ORM attribute access triggers a SQLAlchemy lazy-load
88
+ outside the asyncio greenlet.
89
+
90
+ Covers async-property columns (awaited explicitly by
91
+ ``_populate_async_column``), regular columns / relationships read by
92
+ pydantic's ``from_attributes`` machinery, and any attribute read by
93
+ application code during a request (hooks, filters, custom endpoints),
94
+ which ``globals_middleware_dispatch`` converts through
95
+ ``from_lazy_load``.
96
+
97
+ The most common causes are:
98
+
99
+ - The ORM instance is detached from its session (commit/close happened
100
+ before the schema was rendered).
101
+ - The async session was expired between the query and the
102
+ response-serialisation step.
103
+ - A relationship the schema reads has not been eager-loaded. Prefer
104
+ ``selectinload`` / ``joinedload`` on the query, call
105
+ ``await instance.load("<col>")`` before returning the instance, or
106
+ add the column to ``list_columns`` / ``show_columns`` (or the
107
+ narrower ``list_select_columns`` / ``show_select_columns``, or
108
+ ``extra_list_fetch_columns`` / ``extra_show_fetch_columns`` when
109
+ the column should be fetched but not surfaced in the response) on
110
+ the ``ModelRestApi`` subclass so the toolkit eager-loads it for
111
+ you.
112
+ """
113
+
114
+ def __init__(
115
+ self,
116
+ *,
117
+ model,
118
+ col: str | None,
119
+ source: str | None = None,
120
+ original: Exception,
121
+ ):
122
+ self.model = model
123
+ self.col = col
124
+ self.original = original
125
+ self.source = source
126
+ model_label = type(model).__name__
127
+ col_label = col or "<col>"
128
+ detail = {}
129
+ if col is None:
130
+ detail["problem"] = (
131
+ f"Failed to validate schema for '{model_label}': SQLAlchemy raised "
132
+ f"MissingGreenlet while pydantic read an attribute."
133
+ )
134
+ else:
135
+ detail["problem"] = (
136
+ f"Failed to populate column '{col}' on '{model_label}': SQLAlchemy "
137
+ f"raised MissingGreenlet, the ORM attempted I/O outside the async "
138
+ f"greenlet."
139
+ )
140
+ if source:
141
+ detail["accessed at"] = source
142
+ detail["fix by any of"] = [
143
+ f"Eager-load the relationship: add `selectinload({model_label}.{col_label})` or `joinedload({model_label}.{col_label})` to the query.",
144
+ f'Load it before the instance leaves the async context: `await instance.load("{col_label}")`.',
145
+ f'Add `"{col_label}"` to `list_columns` / `show_columns` on the ModelRestApi subclass, or to the narrower `list_select_columns` / `show_select_columns`.',
146
+ f'Add `"{col_label}"` to `extra_list_fetch_columns` / `extra_show_fetch_columns` when the column should be fetched but not surfaced in the response.',
147
+ ]
148
+ detail["original error"] = repr(original)
149
+ super().__init__("\n" + prettify_dict(detail))
150
+
151
+ @classmethod
152
+ def from_lazy_load(cls, original: sqlalchemy.exc.MissingGreenlet):
153
+ """
154
+ Build an enriched error from a MissingGreenlet raised by an ORM attribute read.
155
+
156
+ Args:
157
+ original (sqlalchemy.exc.MissingGreenlet): The exception SQLAlchemy raised.
158
+
159
+ Returns:
160
+ MissingGreenletError | None: The enriched error, or None when the traceback carries no ORM attribute read to name.
161
+ """
162
+ model, col = _lazy_load_target(original.__traceback__)
163
+ if model is None or col is None:
164
+ return None
165
+ return cls.from_attribute_read(model=model, col=col, original=original)
166
+
167
+ @classmethod
168
+ def from_attribute_read(cls, *, model, col: str | None, original: Exception):
169
+ """
170
+ Build the error for a known model and column, taking the call site from the traceback.
171
+
172
+ Args:
173
+ model: The model whose attribute was read.
174
+ col (str | None): The column that was read, or None when pydantic validation failed on an unnamed attribute.
175
+ original (Exception): The exception SQLAlchemy raised.
176
+
177
+ Returns:
178
+ MissingGreenletError: The error, carrying the call site when the traceback holds one.
179
+ """
180
+ return cls(
181
+ model=model,
182
+ col=col,
183
+ source=_lazy_load_source(original.__traceback__),
184
+ original=original,
185
+ )
186
+
187
+
188
+ def _lazy_load_target(tb: types.TracebackType | None):
189
+ """
190
+ Find the ORM instance and attribute name whose read triggered a lazy load.
191
+
192
+ Args:
193
+ tb (types.TracebackType | None): Traceback of the raised MissingGreenlet.
194
+
195
+ Returns:
196
+ tuple: The ORM instance and its column name, or (None, None) when the traceback holds no attribute read.
197
+ """
198
+ while tb is not None:
199
+ frame = tb.tb_frame
200
+ if (
201
+ frame.f_code.co_name == "__get__"
202
+ and frame.f_globals.get("__name__") == "sqlalchemy.orm.attributes"
203
+ ):
204
+ instance = frame.f_locals.get("instance")
205
+ col = getattr(frame.f_locals.get("self"), "key", None)
206
+ if instance is not None and col:
207
+ return instance, col
208
+ tb = tb.tb_next
209
+ return None, None
210
+
211
+
212
+ def _lazy_load_source(tb: types.TracebackType | None):
213
+ """
214
+ Locate the call site that triggered the lazy load, the last non-toolkit frame before SQLAlchemy took over.
215
+
216
+ Toolkit frames are skipped because the schema-validation probe reads every field itself, so its own line names no code the reader can act on.
217
+
218
+ Args:
219
+ tb (types.TracebackType | None): Traceback of the raised MissingGreenlet.
220
+
221
+ Returns:
222
+ str | None: A "path:line in function" label, or None when only SQLAlchemy and toolkit frames read the attribute.
223
+ """
224
+ caller = None
225
+ while tb is not None:
226
+ module = tb.tb_frame.f_globals.get("__name__", "")
227
+ if module.startswith("sqlalchemy."):
228
+ break
229
+ if not module.startswith("fastapi_rtk."):
230
+ caller = tb
231
+ tb = tb.tb_next
232
+ if caller is None:
233
+ return None
234
+ code = caller.tb_frame.f_code
235
+ return f"{code.co_filename}:{caller.tb_lineno} in {code.co_name}"
@@ -40,6 +40,7 @@ import contextvars
40
40
  import typing
41
41
 
42
42
  import fastapi
43
+ import sqlalchemy.exc
43
44
  import starlette.middleware.base
44
45
  import starlette.types
45
46
  import werkzeug.local
@@ -51,6 +52,7 @@ from .const import (
51
52
  COOKIE_CONFIG,
52
53
  COOKIE_STRATEGY_CONFIG,
53
54
  )
55
+ from .exceptions import MissingGreenletError
54
56
  from .utils import lazy, lazy_import
55
57
 
56
58
  if typing.TYPE_CHECKING:
@@ -240,14 +242,31 @@ async def globals_middleware_dispatch(
240
242
  request: fastapi.Request,
241
243
  call_next: collections.abc.Callable,
242
244
  ) -> fastapi.Response:
243
- """Dispatch the request in a new context to allow globals to be used."""
245
+ """
246
+ Dispatch the request in a new context to allow globals to be used.
247
+
248
+ Args:
249
+ request (fastapi.Request): The incoming request.
250
+ call_next (collections.abc.Callable): The next handler in the middleware chain.
244
251
 
252
+ Raises:
253
+ MissingGreenletError: When a lazy-load of an ORM attribute happened outside the asyncio greenlet, naming the model, the column, and the call site.
254
+
255
+ Returns:
256
+ fastapi.Response: The response produced by the rest of the chain.
257
+ """
245
258
  ctx = contextvars.copy_context()
246
259
 
247
260
  def _call_next() -> collections.abc.Awaitable[fastapi.Response]:
248
261
  return call_next(request)
249
262
 
250
- return await ctx.run(_call_next)
263
+ try:
264
+ return await ctx.run(_call_next)
265
+ except sqlalchemy.exc.MissingGreenlet as e:
266
+ enriched = MissingGreenletError.from_lazy_load(e)
267
+ if not enriched:
268
+ raise
269
+ raise enriched from e
251
270
 
252
271
 
253
272
  class GlobalsMiddleware(starlette.middleware.base.BaseHTTPMiddleware): # noqa