fastapi-rtk 2.12.4__tar.gz → 2.13.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (313) hide show
  1. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/PKG-INFO +1 -1
  2. fastapi_rtk-2.13.1/fastapi_rtk/_version.py +1 -0
  3. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_add_mixin.py +7 -0
  4. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_edit_mixin.py +7 -0
  5. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/generic/column.py +87 -18
  6. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/generic/model.py +18 -5
  7. fastapi_rtk-2.13.1/tests/integration/api/test_add_edit_relation_response.py +250 -0
  8. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/generic/test_column.py +94 -0
  9. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/generic/test_model.py +79 -4
  10. fastapi_rtk-2.12.4/fastapi_rtk/_version.py +0 -1
  11. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/.gitignore +0 -0
  12. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/LICENSE +0 -0
  13. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/README.md +0 -0
  14. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/__init__.py +0 -0
  15. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/__init__.py +0 -0
  16. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/base_api.py +0 -0
  17. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/__init__.py +0 -0
  18. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_bulk_mixin.py +0 -0
  19. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_columns_mixin.py +0 -0
  20. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_delete_mixin.py +0 -0
  21. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_download_mixin.py +0 -0
  22. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_file_mixin.py +0 -0
  23. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_info_mixin.py +0 -0
  24. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_list_mixin.py +0 -0
  25. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_params.py +0 -0
  26. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_show_mixin.py +0 -0
  27. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_typed_base.py +0 -0
  28. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/api/model_rest_api/_upload_mixin.py +0 -0
  29. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/apis/__init__.py +0 -0
  30. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/apis/info.py +0 -0
  31. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/apis/license.py +0 -0
  32. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/__init__.py +0 -0
  33. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/auth.py +0 -0
  34. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/hashers/__init__.py +0 -0
  35. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
  36. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
  37. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/ldap.py +0 -0
  38. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
  39. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
  40. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/strategies/__init__.py +0 -0
  41. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/strategies/config.py +0 -0
  42. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/strategies/db.py +0 -0
  43. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/auth/strategies/jwt.py +0 -0
  44. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/__init__.py +0 -0
  45. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/generic/__init__.py +0 -0
  46. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/generic/db.py +0 -0
  47. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/generic/exceptions.py +0 -0
  48. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/generic/filters.py +0 -0
  49. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/generic/interface.py +0 -0
  50. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/generic/session.py +0 -0
  51. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/__init__.py +0 -0
  52. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/column.py +0 -0
  53. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/db.py +0 -0
  54. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
  55. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
  56. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
  57. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_mixin.py +0 -0
  58. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_mixin.py +0 -0
  59. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
  60. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/audit/_processing_mixin.py +0 -0
  61. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
  62. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
  63. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
  64. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
  65. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
  66. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/filters.py +0 -0
  67. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/interface.py +0 -0
  68. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/model.py +0 -0
  69. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/backends/sqla/session.py +0 -0
  70. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/__init__.py +0 -0
  71. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/cache.py +0 -0
  72. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/db.py +0 -0
  73. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/file_manager.py +0 -0
  74. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/filter.py +0 -0
  75. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/interface.py +0 -0
  76. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/loop_manager.py +0 -0
  77. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/model.py +0 -0
  78. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/session.py +0 -0
  79. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/bases/user_cache.py +0 -0
  80. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/caches/__init__.py +0 -0
  81. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/caches/cache.py +0 -0
  82. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/caches/serializers.py +0 -0
  83. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/caches/storages.py +0 -0
  84. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/__init__.py +0 -0
  85. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/cli.py +0 -0
  86. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/__init__.py +0 -0
  87. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
  88. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
  89. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
  90. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
  91. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
  92. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
  93. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
  94. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
  95. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
  96. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
  97. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/export.py +0 -0
  98. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/security.py +0 -0
  99. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/commands/translate.py +0 -0
  100. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/const.py +0 -0
  101. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/decorators.py +0 -0
  102. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/types.py +0 -0
  103. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/cli/utils.py +0 -0
  104. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/config.py +0 -0
  105. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/const.py +0 -0
  106. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/db.py +0 -0
  107. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/decorators.py +0 -0
  108. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/dependencies.py +0 -0
  109. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/exceptions.py +0 -0
  110. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
  111. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/file_managers/__init__.py +0 -0
  112. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/file_managers/file_manager.py +0 -0
  113. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/file_managers/image_manager.py +0 -0
  114. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
  115. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
  116. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/filters.py +0 -0
  117. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/globals.py +0 -0
  118. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/__init__.py +0 -0
  119. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/babel/__init__.py +0 -0
  120. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/babel/cli.py +0 -0
  121. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/babel/config.py +0 -0
  122. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/babel.cfg +0 -0
  123. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/lazy_text.py +0 -0
  124. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/messages.pot +0 -0
  125. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  126. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
  127. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  128. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
  129. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/loop_managers/__init__.py +0 -0
  130. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/loop_managers/loop_manager.py +0 -0
  131. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/manager.py +0 -0
  132. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/middlewares.py +0 -0
  133. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/mixins.py +0 -0
  134. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/models.py +0 -0
  135. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/py.typed +0 -0
  136. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/registry.py +0 -0
  137. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/routers.py +0 -0
  138. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/schemas.py +0 -0
  139. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/__init__.py +0 -0
  140. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/__init__.py +0 -0
  141. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/apis.py +0 -0
  142. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/models.py +0 -0
  143. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
  144. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/security_manager/_association_mixin.py +0 -0
  145. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/security_manager/_builtin_role_mixin.py +0 -0
  146. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/security_manager/_cleanup_mixin.py +0 -0
  147. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
  148. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
  149. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
  150. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/security_manager/_role_mixin.py +0 -0
  151. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/security_manager/_user_mixin.py +0 -0
  152. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/security/sqla/user_cache.py +0 -0
  153. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/setting.py +0 -0
  154. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/types.py +0 -0
  155. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/__init__.py +0 -0
  156. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/async_task_runner.py +0 -0
  157. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/class_factory.py +0 -0
  158. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/csv_json_converter.py +0 -0
  159. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/deep_merge.py +0 -0
  160. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/extender_mixin.py +0 -0
  161. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
  162. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/formatter.py +0 -0
  163. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/hooks.py +0 -0
  164. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/lazy.py +0 -0
  165. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/merge_schema.py +0 -0
  166. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
  167. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/pydantic.py +0 -0
  168. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/run_utils.py +0 -0
  169. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/self_dependencies.py +0 -0
  170. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
  171. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/sqla.py +0 -0
  172. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/timezone.py +0 -0
  173. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/update_signature.py +0 -0
  174. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/utils/use_default_when_none.py +0 -0
  175. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/fastapi_rtk/version.py +0 -0
  176. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/pyproject.toml +0 -0
  177. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/conftest.py +0 -0
  178. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/docker-compose.gis.yml +0 -0
  179. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/__init__.py +0 -0
  180. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/api/__init__.py +0 -0
  181. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/api/test_model_rest_api.py +0 -0
  182. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/apis/__init__.py +0 -0
  183. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/apis/test_info_callable_schema.py +0 -0
  184. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/apis/test_info_contract.py +0 -0
  185. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/auth/__init__.py +0 -0
  186. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/auth/test_auth_flow.py +0 -0
  187. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/auth/test_oauth.py +0 -0
  188. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/conftest.py +0 -0
  189. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/file_managers/__init__.py +0 -0
  190. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/file_managers/test_file_routes.py +0 -0
  191. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/security/__init__.py +0 -0
  192. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/security/sqla/__init__.py +0 -0
  193. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/security/sqla/test_security_manager.py +0 -0
  194. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/integration/test_app_smoke.py +0 -0
  195. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/__init__.py +0 -0
  196. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/api/__init__.py +0 -0
  197. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/api/conftest.py +0 -0
  198. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/api/model_rest_api/__init__.py +0 -0
  199. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/api/model_rest_api/test_columns.py +0 -0
  200. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
  201. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/api/model_rest_api/test_init.py +0 -0
  202. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/api/model_rest_api/test_typed_base_consistency.py +0 -0
  203. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/api/test_base_api.py +0 -0
  204. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/apis/__init__.py +0 -0
  205. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/apis/test_info.py +0 -0
  206. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/apis/test_license.py +0 -0
  207. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/__init__.py +0 -0
  208. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/hashers/__init__.py +0 -0
  209. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
  210. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/hashers/test_scrypt.py +0 -0
  211. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/password_helpers/__init__.py +0 -0
  212. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/password_helpers/test_fab.py +0 -0
  213. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/strategies/__init__.py +0 -0
  214. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/strategies/test_config.py +0 -0
  215. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/strategies/test_db.py +0 -0
  216. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/strategies/test_jwt.py +0 -0
  217. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/auth/test_auth.py +0 -0
  218. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/__init__.py +0 -0
  219. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/generic/__init__.py +0 -0
  220. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/generic/test_db.py +0 -0
  221. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/generic/test_exceptions.py +0 -0
  222. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/generic/test_filters.py +0 -0
  223. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/generic/test_interface.py +0 -0
  224. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/generic/test_session.py +0 -0
  225. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/__init__.py +0 -0
  226. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/conftest.py +0 -0
  227. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/test_audit.py +0 -0
  228. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/test_audit_types.py +0 -0
  229. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/test_column.py +0 -0
  230. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/test_db.py +0 -0
  231. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/test_filters.py +0 -0
  232. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
  233. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/test_interface.py +0 -0
  234. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/test_model.py +0 -0
  235. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/backends/sqla/test_session.py +0 -0
  236. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/bases/__init__.py +0 -0
  237. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/bases/test_db.py +0 -0
  238. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/bases/test_file_manager.py +0 -0
  239. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/bases/test_filter.py +0 -0
  240. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/bases/test_interface.py +0 -0
  241. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/bases/test_model.py +0 -0
  242. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/bases/test_session.py +0 -0
  243. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/__init__.py +0 -0
  244. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/commands/__init__.py +0 -0
  245. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/commands/conftest.py +0 -0
  246. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/commands/test_db.py +0 -0
  247. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/commands/test_export.py +0 -0
  248. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/commands/test_security.py +0 -0
  249. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/commands/test_security_crypto.py +0 -0
  250. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/commands/test_translate.py +0 -0
  251. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/conftest.py +0 -0
  252. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/test_cli.py +0 -0
  253. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/test_decorators.py +0 -0
  254. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/test_types.py +0 -0
  255. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/cli/test_utils.py +0 -0
  256. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/file_managers/__init__.py +0 -0
  257. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/file_managers/conftest.py +0 -0
  258. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/file_managers/test_file_manager.py +0 -0
  259. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/file_managers/test_image_manager.py +0 -0
  260. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
  261. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
  262. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/lang/__init__.py +0 -0
  263. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/lang/test_babel_cli.py +0 -0
  264. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/lang/test_babel_config.py +0 -0
  265. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/lang/test_lazy_text.py +0 -0
  266. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/security/__init__.py +0 -0
  267. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/security/sqla/__init__.py +0 -0
  268. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/security/sqla/conftest.py +0 -0
  269. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/security/sqla/test_apis.py +0 -0
  270. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/security/sqla/test_models.py +0 -0
  271. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/security/sqla/test_security_manager.py +0 -0
  272. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_caches.py +0 -0
  273. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_config.py +0 -0
  274. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_const.py +0 -0
  275. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_db.py +0 -0
  276. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_decorators.py +0 -0
  277. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_dependencies.py +0 -0
  278. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_exceptions.py +0 -0
  279. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_fastapi_react_toolkit.py +0 -0
  280. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_filters.py +0 -0
  281. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_globals.py +0 -0
  282. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_manager.py +0 -0
  283. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_middlewares.py +0 -0
  284. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_mixins.py +0 -0
  285. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_models.py +0 -0
  286. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_registry.py +0 -0
  287. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_routers.py +0 -0
  288. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_schemas.py +0 -0
  289. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_setting.py +0 -0
  290. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_types.py +0 -0
  291. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_user_cache.py +0 -0
  292. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/test_version.py +0 -0
  293. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/__init__.py +0 -0
  294. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_async_task_runner.py +0 -0
  295. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_class_factory.py +0 -0
  296. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_csv_json_converter.py +0 -0
  297. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_deep_merge.py +0 -0
  298. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_extender_mixin.py +0 -0
  299. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
  300. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_formatter.py +0 -0
  301. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_hooks.py +0 -0
  302. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_lazy.py +0 -0
  303. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_merge_schema.py +0 -0
  304. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
  305. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_pydantic.py +0 -0
  306. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_run_utils.py +0 -0
  307. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_self_dependencies.py +0 -0
  308. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_smartdefaultdict.py +0 -0
  309. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_sqla.py +0 -0
  310. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_timezone.py +0 -0
  311. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_update_signature.py +0 -0
  312. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/tests/unit/utils/test_use_default_when_none.py +0 -0
  313. {fastapi_rtk-2.12.4 → fastapi_rtk-2.13.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fastapi-rtk
3
- Version: 2.12.4
3
+ Version: 2.13.1
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.1"
@@ -83,6 +83,13 @@ class AddMixin(_TypedModelRestApi):
83
83
  after_commit_runner.remove_tasks_by_tag(
84
84
  "file"
85
85
  ) # Delete any file tasks scheduled to revert files on error
86
+ pk, _ = self.datamodel.convert_to_result(item)
87
+ # Re-read without base filters: a row the caller may not list is still the row it just wrote.
88
+ item = await smart_run(
89
+ self.datamodel.get_one,
90
+ session,
91
+ params={"list_columns": self.show_select_columns, "where_id": pk},
92
+ )
86
93
  post_add = await smart_run(
87
94
  self.post_add,
88
95
  item,
@@ -108,6 +108,13 @@ class EditMixin(_TypedModelRestApi):
108
108
  after_commit_runner.remove_tasks_by_tag(
109
109
  "file"
110
110
  ) # Delete any file tasks scheduled to revert files on error
111
+ pk, _ = self.datamodel.convert_to_result(item)
112
+ # Re-read without base filters: a row the caller may not list is still the row it just wrote.
113
+ item = await smart_run(
114
+ self.datamodel.get_one,
115
+ session,
116
+ params={"list_columns": self.show_select_columns, "where_id": pk},
117
+ )
111
118
  post_update = await smart_run(
112
119
  self.post_update,
113
120
  item,
@@ -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)})"
@@ -0,0 +1,250 @@
1
+ """Regression tests for relation loading in the add / edit responses.
2
+
3
+ Both responses serialize `show_select_columns`, which includes relationships
4
+ that neither `SQLAInterface.add` nor `.edit` leaves loaded, so the routes
5
+ re-read the item through `ModelRestApi._reload_item` before serializing.
6
+ """
7
+
8
+ import contextlib
9
+
10
+ import httpx
11
+ import pytest
12
+ import pytest_asyncio
13
+ import sqlalchemy
14
+ import sqlalchemy.ext.asyncio
15
+
16
+ from fastapi_rtk.api.model_rest_api import ModelRestApi
17
+ from fastapi_rtk.backends.sqla.interface import SQLAInterface
18
+ from fastapi_rtk.backends.sqla.model import Model
19
+
20
+ pytestmark = pytest.mark.asyncio
21
+
22
+
23
+ class LazyContinent(Model):
24
+ __tablename__ = "lazy_continent"
25
+ id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
26
+ name = sqlalchemy.Column(sqlalchemy.String(50), nullable=False)
27
+ countries = sqlalchemy.orm.relationship(
28
+ "LazyCountry", back_populates="continent", lazy="select"
29
+ )
30
+
31
+
32
+ class LazyCountry(Model):
33
+ __tablename__ = "lazy_country"
34
+ id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
35
+ name = sqlalchemy.Column(sqlalchemy.String(50), nullable=False)
36
+ continent_id = sqlalchemy.Column(
37
+ sqlalchemy.Integer, sqlalchemy.ForeignKey("lazy_continent.id")
38
+ )
39
+ continent = sqlalchemy.orm.relationship(
40
+ "LazyContinent", back_populates="countries", lazy="select"
41
+ )
42
+ widgets = sqlalchemy.orm.relationship(
43
+ "LazyWidget", back_populates="country", lazy="select"
44
+ )
45
+
46
+
47
+ class LazyWidget(Model):
48
+ __tablename__ = "lazy_widget"
49
+ id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
50
+ name = sqlalchemy.Column(sqlalchemy.String(50), nullable=False)
51
+ country_id = sqlalchemy.Column(
52
+ sqlalchemy.Integer, sqlalchemy.ForeignKey("lazy_country.id")
53
+ )
54
+ country = sqlalchemy.orm.relationship(
55
+ "LazyCountry", back_populates="widgets", lazy="select"
56
+ )
57
+
58
+
59
+ class LazyContinentsApi(ModelRestApi):
60
+ resource_name = "lazycontinents"
61
+ datamodel = SQLAInterface(LazyContinent)
62
+
63
+
64
+ class LazyCountriesApi(ModelRestApi):
65
+ resource_name = "lazycountries"
66
+ datamodel = SQLAInterface(LazyCountry)
67
+
68
+
69
+ class LazyWidgetsApi(ModelRestApi):
70
+ resource_name = "lazywidgets"
71
+ datamodel = SQLAInterface(LazyWidget)
72
+
73
+
74
+ @pytest_asyncio.fixture
75
+ async def lazy_app(make_toolkit_app):
76
+ import sys
77
+
78
+ @contextlib.asynccontextmanager
79
+ async def lazy_lifespan(_app):
80
+ engine = sys.modules["fastapi_rtk.db"].db._engine
81
+ async with engine.begin() as conn:
82
+ for model in (LazyContinent, LazyCountry, LazyWidget):
83
+ await conn.run_sync(
84
+ lambda c, m=model: m.__table__.create(c, checkfirst=True)
85
+ )
86
+ yield
87
+
88
+ return make_toolkit_app(
89
+ extra_apis=[LazyContinentsApi, LazyCountriesApi, LazyWidgetsApi],
90
+ extra_lifespans=[lazy_lifespan],
91
+ )
92
+
93
+
94
+ @pytest_asyncio.fixture
95
+ async def lazy_client(lazy_app):
96
+ transport = httpx.ASGITransport(app=lazy_app)
97
+ async with lazy_app.router.lifespan_context(lazy_app):
98
+ async with httpx.AsyncClient(
99
+ transport=transport, base_url="http://testserver"
100
+ ) as ac:
101
+ yield ac
102
+
103
+
104
+ @pytest_asyncio.fixture
105
+ async def token(lazy_client):
106
+ import sys
107
+
108
+ db = sys.modules["fastapi_rtk.db"].db
109
+ from fastapi_rtk.globals import g
110
+
111
+ sm = g.current_app.sm
112
+ async with db.session() as session:
113
+ user = await sm.create_user(
114
+ username="admin",
115
+ email="admin@example.com",
116
+ password="adminpass123",
117
+ roles=["Admin"],
118
+ session=session,
119
+ raise_exception=True,
120
+ )
121
+ assert user is not None
122
+ response = await lazy_client.post(
123
+ "/api/v1/auth/jwt/login",
124
+ data={"username": "admin", "password": "adminpass123"},
125
+ )
126
+ assert response.status_code == 200, response.text
127
+ return response.json()["access_token"]
128
+
129
+
130
+ @pytest.fixture
131
+ def countries_api(lazy_app):
132
+ from fastapi_rtk.globals import g
133
+
134
+ return next(a for a in g.current_app.apis if a.resource_name == "lazycountries")
135
+
136
+
137
+ class TestShowColumns:
138
+ async def test_collection_and_relation_in_show_columns(self, countries_api):
139
+ assert "widgets" in countries_api.show_select_columns
140
+ assert "continent" in countries_api.show_select_columns
141
+
142
+
143
+ @pytest_asyncio.fixture
144
+ async def seeded(lazy_client):
145
+ """Insert a continent + country + widget chain directly, bypassing the routes."""
146
+ import sys
147
+
148
+ db = sys.modules["fastapi_rtk.db"].db
149
+ async with db.session() as session:
150
+ continent = LazyContinent(name="Europe")
151
+ country = LazyCountry(name="Germany", continent=continent)
152
+ country.widgets.append(LazyWidget(name="Hammer"))
153
+ session.add(country)
154
+ await session.commit()
155
+ return country.id
156
+
157
+
158
+ class TestReadRoutes:
159
+ async def test_list_and_show_load_relations(self, lazy_client, token, seeded):
160
+ country_id = seeded
161
+
162
+ listing = await lazy_client.get(
163
+ "/api/v1/lazycountries/",
164
+ headers={"Authorization": f"Bearer {token}"},
165
+ )
166
+ assert listing.status_code == 200, listing.text
167
+ show = await lazy_client.get(
168
+ f"/api/v1/lazycountries/{country_id}",
169
+ headers={"Authorization": f"Bearer {token}"},
170
+ )
171
+ assert show.status_code == 200, show.text
172
+
173
+
174
+ class TestAdd:
175
+ async def test_post_model_with_only_a_collection(self, lazy_client, token):
176
+ response = await lazy_client.post(
177
+ "/api/v1/lazycontinents/",
178
+ headers={"Authorization": f"Bearer {token}"},
179
+ json={"name": "Europe"},
180
+ )
181
+ assert response.status_code == 200, response.text
182
+ assert response.json()["result"]["countries"] == []
183
+
184
+ async def test_post_serializes_unloaded_collection(self, lazy_client, token):
185
+ response = await lazy_client.post(
186
+ "/api/v1/lazycountries/",
187
+ headers={"Authorization": f"Bearer {token}"},
188
+ json={"name": "Germany"},
189
+ )
190
+ assert response.status_code == 200, response.text
191
+ assert response.json()["result"]["widgets"] == []
192
+
193
+
194
+ class TestBuiltinRolesApi:
195
+ async def test_post_role(self, client, admin_token):
196
+ response = await client.post(
197
+ "/api/v1/roles/",
198
+ headers={"Authorization": f"Bearer {admin_token}"},
199
+ json={"name": "Tester"},
200
+ )
201
+ assert response.status_code == 200, response.text
202
+
203
+
204
+ class TestEdit:
205
+ async def test_put_without_relation_serializes_relations(self, lazy_client, token):
206
+ continent = await lazy_client.post(
207
+ "/api/v1/lazycontinents/",
208
+ headers={"Authorization": f"Bearer {token}"},
209
+ json={"name": "Europe"},
210
+ )
211
+ assert continent.status_code == 200, continent.text
212
+ post = await lazy_client.post(
213
+ "/api/v1/lazycountries/",
214
+ headers={"Authorization": f"Bearer {token}"},
215
+ json={"name": "Germany", "continent": continent.json()["id"]},
216
+ )
217
+ assert post.status_code == 200, post.text
218
+ country_id = post.json()["id"]
219
+ widget = await lazy_client.post(
220
+ "/api/v1/lazywidgets/",
221
+ headers={"Authorization": f"Bearer {token}"},
222
+ json={"name": "Hammer", "country": country_id},
223
+ )
224
+ assert widget.status_code == 200, widget.text
225
+
226
+ response = await lazy_client.put(
227
+ f"/api/v1/lazycountries/{country_id}",
228
+ headers={"Authorization": f"Bearer {token}"},
229
+ json={"name": "Deutschland"},
230
+ )
231
+ assert response.status_code == 200, response.text
232
+ body = response.json()["result"]
233
+ assert body["continent"] is not None
234
+ assert len(body["widgets"]) == 1
235
+
236
+
237
+ class TestBuiltinUsersApi:
238
+ async def test_post_user(self, client, admin_token):
239
+ response = await client.post(
240
+ "/api/v1/users/",
241
+ headers={"Authorization": f"Bearer {admin_token}"},
242
+ json={
243
+ "username": "bob",
244
+ "email": "bob@example.com",
245
+ "password": "bobpass123",
246
+ "first_name": "Bob",
247
+ "last_name": "Builder",
248
+ },
249
+ )
250
+ assert response.status_code == 200, response.text
@@ -1,4 +1,7 @@
1
+ import datetime
2
+
1
3
  import pytest
4
+
2
5
  from fastapi_rtk.backends.generic.column import (
3
6
  PK_AUTO_INCREMENT,
4
7
  UNSET,
@@ -8,6 +11,7 @@ from fastapi_rtk.backends.generic.column import (
8
11
  )
9
12
  from fastapi_rtk.backends.generic.exceptions import (
10
13
  ColumnInvalidTypeException,
14
+ ColumnNotSetException,
11
15
  GenericColumnException,
12
16
  )
13
17
  from fastapi_rtk.backends.generic.model import GenericModel
@@ -54,6 +58,28 @@ class TestColumnInit:
54
58
  col = GenericColumn(int, default=with_instance)
55
59
  assert col.default is with_instance
56
60
 
61
+ def test_default_hashable_value_kept_as_is(self):
62
+ col = GenericColumn(int, default=99)
63
+ assert col.default == 99
64
+
65
+ def test_default_builtin_with_optional_arg_wrapped(self):
66
+ col = GenericColumn(list, default=list)
67
+ assert col.default(None) == []
68
+
69
+ def test_default_builtin_without_signature_wrapped(self):
70
+ col = GenericColumn(dict, default=dict)
71
+ assert col.default(None) == {}
72
+
73
+ def test_default_builtin_method_wrapped(self):
74
+ col = GenericColumn(datetime.datetime, default=datetime.datetime.now)
75
+ assert isinstance(col.default(None), datetime.datetime)
76
+
77
+ def test_default_mutable_value_wrapped(self):
78
+ col = GenericColumn(list, default=[])
79
+ first = col.default(None)
80
+ assert first == []
81
+ assert first is not col.default(None)
82
+
57
83
 
58
84
  class TestColumnDescriptorAccess:
59
85
  def _make_model(self, **column_kwargs):
@@ -94,6 +120,12 @@ class TestColumnDescriptorAccess:
94
120
  m.x = 7
95
121
  assert m.x == 7
96
122
 
123
+ def test_get_unset_not_nullable_raises(self):
124
+ M = self._make_model(nullable=False)
125
+ m = M(__check_on_init__=False)
126
+ with pytest.raises(ColumnNotSetException, match="is not set"):
127
+ m.x
128
+
97
129
  def test_pk_auto_increment_returns_sentinel_when_not_set(self):
98
130
  M = self._make_model()
99
131
  m = M(__check_on_init__=False)
@@ -101,6 +133,40 @@ class TestColumnDescriptorAccess:
101
133
  assert m.id is PK_AUTO_INCREMENT
102
134
 
103
135
 
136
+ class TestMutableDefault:
137
+ def _make_model(self, **column_kwargs):
138
+ class M(GenericModel):
139
+ id = GenericColumn(int, primary_key=True, auto_increment=True)
140
+ x = GenericColumn(list, **column_kwargs)
141
+
142
+ return M
143
+
144
+ def test_mutation_does_not_leak_between_instances(self):
145
+ M = self._make_model(default=[])
146
+ first = M(__check_on_init__=False)
147
+ second = M(__check_on_init__=False)
148
+ first.x.append("leak")
149
+ assert second.x == []
150
+
151
+ def test_nested_mutation_does_not_leak_between_instances(self):
152
+ M = self._make_model(default=[[]])
153
+ first = M(__check_on_init__=False)
154
+ second = M(__check_on_init__=False)
155
+ first.x[0].append("leak")
156
+ assert second.x == [[]]
157
+
158
+ def test_builtin_factory_default_is_read(self):
159
+ M = self._make_model(default=list)
160
+ m = M(__check_on_init__=False)
161
+ assert m.x == []
162
+
163
+ def test_callable_default_rejected_as_value(self):
164
+ M = self._make_model(default=[])
165
+ m = M(__check_on_init__=False)
166
+ with pytest.raises(ColumnInvalidTypeException):
167
+ m.x = list
168
+
169
+
104
170
  class TestIsColumnSet:
105
171
  def test_unset_returns_false(self):
106
172
  class M(GenericModel):
@@ -120,6 +186,34 @@ class TestIsColumnSet:
120
186
  assert M.x.is_column_set(m) is True
121
187
 
122
188
 
189
+ class TestApplyDefault:
190
+ def _make_model(self, **column_kwargs):
191
+ class M(GenericModel):
192
+ id = GenericColumn(int, primary_key=True, auto_increment=True)
193
+ x = GenericColumn(int, **column_kwargs)
194
+
195
+ return M
196
+
197
+ def test_no_default_writes_nothing(self):
198
+ M = self._make_model()
199
+ m = M(__check_on_init__=False)
200
+ M.x.apply_default(m)
201
+ assert M.x.is_column_set(m) is False
202
+
203
+ def test_set_column_is_not_overwritten(self):
204
+ M = self._make_model(default=99)
205
+ m = M(__check_on_init__=False)
206
+ m.x = 1
207
+ M.x.apply_default(m)
208
+ assert m.x == 1
209
+
210
+ def test_sentinel_result_is_not_written(self):
211
+ M = self._make_model(default=lambda instance: instance.id)
212
+ m = M(__check_on_init__=False)
213
+ M.x.apply_default(m)
214
+ assert M.x.is_column_set(m) is False
215
+
216
+
123
217
  class TestRepr:
124
218
  def test_repr_includes_attrs(self):
125
219
  col = GenericColumn(int, primary_key=True)