fastapi-rtk 2.10.2__tar.gz → 2.11.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 (301) hide show
  1. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/PKG-INFO +1 -1
  2. fastapi_rtk-2.11.1/fastapi_rtk/_version.py +1 -0
  3. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/generic/filters.py +5 -1
  4. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/filters.py +39 -17
  5. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/cli.py +45 -1
  6. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/models.py +9 -3
  7. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/generic/test_filters.py +12 -1
  8. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/test_filters.py +60 -0
  9. fastapi_rtk-2.11.1/tests/unit/cli/test_cli.py +172 -0
  10. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/security/sqla/test_models.py +7 -0
  11. fastapi_rtk-2.10.2/fastapi_rtk/_version.py +0 -1
  12. fastapi_rtk-2.10.2/tests/unit/cli/test_cli.py +0 -117
  13. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/.gitignore +0 -0
  14. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/LICENSE +0 -0
  15. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/README.md +0 -0
  16. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/__init__.py +0 -0
  17. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/__init__.py +0 -0
  18. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/base_api.py +0 -0
  19. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/__init__.py +0 -0
  20. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_add_mixin.py +0 -0
  21. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_bulk_mixin.py +0 -0
  22. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_columns_mixin.py +0 -0
  23. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_delete_mixin.py +0 -0
  24. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_download_mixin.py +0 -0
  25. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_edit_mixin.py +0 -0
  26. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_file_mixin.py +0 -0
  27. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_info_mixin.py +0 -0
  28. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_list_mixin.py +0 -0
  29. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_params.py +0 -0
  30. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_show_mixin.py +0 -0
  31. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_typed_base.py +0 -0
  32. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/api/model_rest_api/_upload_mixin.py +0 -0
  33. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/apis/__init__.py +0 -0
  34. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/apis/info.py +0 -0
  35. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/apis/license.py +0 -0
  36. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/__init__.py +0 -0
  37. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/auth.py +0 -0
  38. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/hashers/__init__.py +0 -0
  39. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
  40. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
  41. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/ldap.py +0 -0
  42. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
  43. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
  44. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/strategies/__init__.py +0 -0
  45. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/strategies/config.py +0 -0
  46. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/strategies/db.py +0 -0
  47. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/auth/strategies/jwt.py +0 -0
  48. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/__init__.py +0 -0
  49. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/generic/__init__.py +0 -0
  50. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/generic/column.py +0 -0
  51. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/generic/db.py +0 -0
  52. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/generic/exceptions.py +0 -0
  53. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/generic/interface.py +0 -0
  54. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/generic/model.py +0 -0
  55. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/generic/session.py +0 -0
  56. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/__init__.py +0 -0
  57. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/column.py +0 -0
  58. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/db.py +0 -0
  59. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
  60. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
  61. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
  62. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_mixin.py +0 -0
  63. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_mixin.py +0 -0
  64. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
  65. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/audit/_processing_mixin.py +0 -0
  66. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
  67. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
  68. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
  69. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
  70. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
  71. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/interface.py +0 -0
  72. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/model.py +0 -0
  73. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/backends/sqla/session.py +0 -0
  74. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/bases/__init__.py +0 -0
  75. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/bases/db.py +0 -0
  76. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/bases/file_manager.py +0 -0
  77. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/bases/filter.py +0 -0
  78. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/bases/interface.py +0 -0
  79. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/bases/model.py +0 -0
  80. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/bases/session.py +0 -0
  81. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/__init__.py +0 -0
  82. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/__init__.py +0 -0
  83. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
  84. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
  85. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
  86. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
  87. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
  88. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
  89. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
  90. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
  91. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
  92. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
  93. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/export.py +0 -0
  94. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/security.py +0 -0
  95. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/commands/translate.py +0 -0
  96. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/const.py +0 -0
  97. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/decorators.py +0 -0
  98. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/types.py +0 -0
  99. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/cli/utils.py +0 -0
  100. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/config.py +0 -0
  101. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/const.py +0 -0
  102. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/db.py +0 -0
  103. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/decorators.py +0 -0
  104. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/dependencies.py +0 -0
  105. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/exceptions.py +0 -0
  106. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
  107. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/file_managers/__init__.py +0 -0
  108. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/file_managers/file_manager.py +0 -0
  109. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/file_managers/image_manager.py +0 -0
  110. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
  111. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
  112. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/filters.py +0 -0
  113. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/globals.py +0 -0
  114. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/__init__.py +0 -0
  115. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/babel/__init__.py +0 -0
  116. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/babel/cli.py +0 -0
  117. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/babel/config.py +0 -0
  118. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/babel.cfg +0 -0
  119. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/lazy_text.py +0 -0
  120. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/messages.pot +0 -0
  121. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  122. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
  123. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  124. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
  125. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/manager.py +0 -0
  126. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/middlewares.py +0 -0
  127. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/mixins.py +0 -0
  128. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/models.py +0 -0
  129. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/py.typed +0 -0
  130. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/registry.py +0 -0
  131. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/routers.py +0 -0
  132. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/schemas.py +0 -0
  133. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/__init__.py +0 -0
  134. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/__init__.py +0 -0
  135. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/apis.py +0 -0
  136. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
  137. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/security_manager/_association_mixin.py +0 -0
  138. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/security_manager/_builtin_role_mixin.py +0 -0
  139. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/security_manager/_cleanup_mixin.py +0 -0
  140. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
  141. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
  142. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
  143. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/security_manager/_role_mixin.py +0 -0
  144. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/security/sqla/security_manager/_user_mixin.py +0 -0
  145. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/setting.py +0 -0
  146. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/types.py +0 -0
  147. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/__init__.py +0 -0
  148. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/async_task_runner.py +0 -0
  149. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/class_factory.py +0 -0
  150. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/csv_json_converter.py +0 -0
  151. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/deep_merge.py +0 -0
  152. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/extender_mixin.py +0 -0
  153. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
  154. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/formatter.py +0 -0
  155. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/hooks.py +0 -0
  156. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/lazy.py +0 -0
  157. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/merge_schema.py +0 -0
  158. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
  159. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/pydantic.py +0 -0
  160. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/run_utils.py +0 -0
  161. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/self_dependencies.py +0 -0
  162. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
  163. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/sqla.py +0 -0
  164. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/timezone.py +0 -0
  165. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/update_signature.py +0 -0
  166. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/utils/use_default_when_none.py +0 -0
  167. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/fastapi_rtk/version.py +0 -0
  168. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/pyproject.toml +0 -0
  169. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/conftest.py +0 -0
  170. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/docker-compose.gis.yml +0 -0
  171. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/__init__.py +0 -0
  172. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/api/__init__.py +0 -0
  173. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/api/test_model_rest_api.py +0 -0
  174. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/apis/__init__.py +0 -0
  175. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/apis/test_info_callable_schema.py +0 -0
  176. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/apis/test_info_contract.py +0 -0
  177. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/auth/__init__.py +0 -0
  178. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/auth/test_auth_flow.py +0 -0
  179. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/auth/test_oauth.py +0 -0
  180. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/conftest.py +0 -0
  181. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/file_managers/__init__.py +0 -0
  182. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/file_managers/test_file_routes.py +0 -0
  183. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/security/__init__.py +0 -0
  184. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/security/sqla/__init__.py +0 -0
  185. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/security/sqla/test_security_manager.py +0 -0
  186. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/integration/test_app_smoke.py +0 -0
  187. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/__init__.py +0 -0
  188. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/api/__init__.py +0 -0
  189. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/api/conftest.py +0 -0
  190. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/api/model_rest_api/__init__.py +0 -0
  191. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/api/model_rest_api/test_columns.py +0 -0
  192. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
  193. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/api/model_rest_api/test_init.py +0 -0
  194. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/api/model_rest_api/test_typed_base_consistency.py +0 -0
  195. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/api/test_base_api.py +0 -0
  196. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/apis/__init__.py +0 -0
  197. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/apis/test_info.py +0 -0
  198. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/apis/test_license.py +0 -0
  199. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/__init__.py +0 -0
  200. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/hashers/__init__.py +0 -0
  201. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
  202. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/hashers/test_scrypt.py +0 -0
  203. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/password_helpers/__init__.py +0 -0
  204. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/password_helpers/test_fab.py +0 -0
  205. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/strategies/__init__.py +0 -0
  206. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/strategies/test_config.py +0 -0
  207. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/strategies/test_db.py +0 -0
  208. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/strategies/test_jwt.py +0 -0
  209. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/auth/test_auth.py +0 -0
  210. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/__init__.py +0 -0
  211. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/generic/__init__.py +0 -0
  212. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/generic/test_column.py +0 -0
  213. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/generic/test_db.py +0 -0
  214. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/generic/test_exceptions.py +0 -0
  215. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/generic/test_interface.py +0 -0
  216. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/generic/test_model.py +0 -0
  217. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/generic/test_session.py +0 -0
  218. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/__init__.py +0 -0
  219. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/conftest.py +0 -0
  220. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/test_audit.py +0 -0
  221. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/test_audit_types.py +0 -0
  222. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/test_column.py +0 -0
  223. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/test_db.py +0 -0
  224. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
  225. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/test_interface.py +0 -0
  226. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/test_model.py +0 -0
  227. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/backends/sqla/test_session.py +0 -0
  228. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/bases/__init__.py +0 -0
  229. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/bases/test_db.py +0 -0
  230. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/bases/test_file_manager.py +0 -0
  231. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/bases/test_filter.py +0 -0
  232. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/bases/test_interface.py +0 -0
  233. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/bases/test_model.py +0 -0
  234. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/bases/test_session.py +0 -0
  235. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/__init__.py +0 -0
  236. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/commands/__init__.py +0 -0
  237. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/commands/conftest.py +0 -0
  238. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/commands/test_db.py +0 -0
  239. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/commands/test_export.py +0 -0
  240. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/commands/test_security.py +0 -0
  241. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/commands/test_security_crypto.py +0 -0
  242. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/commands/test_translate.py +0 -0
  243. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/conftest.py +0 -0
  244. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/test_decorators.py +0 -0
  245. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/test_types.py +0 -0
  246. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/cli/test_utils.py +0 -0
  247. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/file_managers/__init__.py +0 -0
  248. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/file_managers/conftest.py +0 -0
  249. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/file_managers/test_file_manager.py +0 -0
  250. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/file_managers/test_image_manager.py +0 -0
  251. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
  252. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
  253. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/lang/__init__.py +0 -0
  254. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/lang/test_babel_cli.py +0 -0
  255. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/lang/test_babel_config.py +0 -0
  256. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/lang/test_lazy_text.py +0 -0
  257. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/security/__init__.py +0 -0
  258. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/security/sqla/__init__.py +0 -0
  259. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/security/sqla/conftest.py +0 -0
  260. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/security/sqla/test_apis.py +0 -0
  261. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/security/sqla/test_security_manager.py +0 -0
  262. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_config.py +0 -0
  263. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_const.py +0 -0
  264. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_db.py +0 -0
  265. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_decorators.py +0 -0
  266. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_dependencies.py +0 -0
  267. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_exceptions.py +0 -0
  268. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_fastapi_react_toolkit.py +0 -0
  269. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_filters.py +0 -0
  270. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_globals.py +0 -0
  271. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_manager.py +0 -0
  272. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_middlewares.py +0 -0
  273. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_mixins.py +0 -0
  274. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_models.py +0 -0
  275. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_registry.py +0 -0
  276. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_routers.py +0 -0
  277. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_schemas.py +0 -0
  278. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_setting.py +0 -0
  279. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_types.py +0 -0
  280. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/test_version.py +0 -0
  281. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/__init__.py +0 -0
  282. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_async_task_runner.py +0 -0
  283. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_class_factory.py +0 -0
  284. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_csv_json_converter.py +0 -0
  285. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_deep_merge.py +0 -0
  286. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_extender_mixin.py +0 -0
  287. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
  288. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_formatter.py +0 -0
  289. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_hooks.py +0 -0
  290. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_lazy.py +0 -0
  291. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_merge_schema.py +0 -0
  292. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
  293. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_pydantic.py +0 -0
  294. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_run_utils.py +0 -0
  295. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_self_dependencies.py +0 -0
  296. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_smartdefaultdict.py +0 -0
  297. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_sqla.py +0 -0
  298. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_timezone.py +0 -0
  299. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_update_signature.py +0 -0
  300. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/tests/unit/utils/test_use_default_when_none.py +0 -0
  301. {fastapi_rtk-2.10.2 → fastapi_rtk-2.11.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-rtk
3
- Version: 2.10.2
3
+ Version: 2.11.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.11.1"
@@ -64,7 +64,11 @@ class GenericBaseFilter(AbstractBaseFilter[GenericSession]):
64
64
  if self.datamodel.is_float(col):
65
65
  return float(value)
66
66
  if self.datamodel.is_boolean(col):
67
- return value == 1 or value.lower() in ["true", "1"]
67
+ if isinstance(value, bool):
68
+ return value
69
+ return value == 1 or (
70
+ isinstance(value, str) and value.lower() in ["true", "1"]
71
+ )
68
72
  if self.datamodel.is_date(col):
69
73
  return datetime.datetime.fromisoformat(value).date()
70
74
  if self.datamodel.is_datetime(col):
@@ -42,6 +42,10 @@ __all__ = [
42
42
  ]
43
43
 
44
44
 
45
+ # Oracle needs a length on the CAST target: CAST(x AS VARCHAR2) is ORA-00906.
46
+ _TEXT_CAST_TYPE = sa_types.String().with_variant(sa_types.String(4000), "oracle")
47
+
48
+
45
49
  class BaseFilter(AbstractBaseFilter[sqlalchemy.Select[Model]]):
46
50
  def _get_column(self, col_name: str) -> sqlalchemy.Column:
47
51
  """
@@ -121,7 +125,11 @@ class BaseFilter(AbstractBaseFilter[sqlalchemy.Select[Model]]):
121
125
  if self.datamodel.is_float(col):
122
126
  return float(value)
123
127
  if self.datamodel.is_boolean(col):
124
- return value == 1 or value.lower() in ["true", "1"]
128
+ if isinstance(value, bool):
129
+ return value
130
+ return value == 1 or (
131
+ isinstance(value, str) and value.lower() in ["true", "1"]
132
+ )
125
133
  if self.datamodel.is_date(
126
134
  col
127
135
  ): # pragma: no cover - exercised via integration filter tests only
@@ -139,6 +147,28 @@ class BaseFilter(AbstractBaseFilter[sqlalchemy.Select[Model]]):
139
147
 
140
148
 
141
149
  class BaseFilterTextContains(BaseFilter):
150
+ def _build_concat_expr(self, col_objs: list[sqlalchemy.ColumnElement]):
151
+ """
152
+ Concatenate columns as text, separated by a space.
153
+
154
+ Chains ``||`` instead of ``concat(*args)``: Oracle's CONCAT takes exactly two
155
+ arguments and other databases cap function-call args, which wide tables exceed.
156
+ ``coalesce`` keeps concat's NULL-as-empty.
157
+
158
+ Args:
159
+ col_objs (list[sqlalchemy.ColumnElement]): Columns to concatenate.
160
+
161
+ Returns:
162
+ sqlalchemy.ColumnElement | None: The concatenation, or None when col_objs is empty.
163
+ """
164
+ concat_expr = None
165
+ for col_obj in col_objs:
166
+ piece = sqlalchemy.func.coalesce(
167
+ sqlalchemy.cast(col_obj, _TEXT_CAST_TYPE), ""
168
+ )
169
+ concat_expr = piece if concat_expr is None else concat_expr + " " + piece
170
+ return concat_expr
171
+
142
172
  def _handle_relation_contains(self, col: str, value: str):
143
173
  sub_col = ""
144
174
  if "." in col:
@@ -164,16 +194,14 @@ class BaseFilterTextContains(BaseFilter):
164
194
  else:
165
195
  # If there is no sub-column, we filter all columns in the relation
166
196
  rel_cols = rel_interface.list_columns.keys()
167
- concat = []
168
197
  rel_obj = rel_interface.obj
169
198
  rel_pks = rel_interface.get_pk_attrs()
170
- for col_name in rel_cols:
171
- col_obj = getattr(rel_obj, col_name)
172
- concat.append(sqlalchemy.cast(col_obj, sa_types.String))
173
- concat.append(" ")
199
+ concat_expr = self._build_concat_expr(
200
+ [getattr(rel_obj, col_name) for col_name in rel_cols]
201
+ )
174
202
  rel_statements = [
175
203
  sqlalchemy.select(getattr(rel_obj, pk)).filter(
176
- sqlalchemy.func.concat(*concat).ilike("%" + value + "%")
204
+ concat_expr.ilike("%" + value + "%")
177
205
  )
178
206
  for pk in rel_pks
179
207
  ]
@@ -206,7 +234,7 @@ class BaseFilterTextContains(BaseFilter):
206
234
  if any(value in keywords for keywords in true_keywords)
207
235
  else False
208
236
  )
209
- return sqlalchemy.cast(self._cast_column(col), sa_types.String).ilike(
237
+ return sqlalchemy.cast(self._cast_column(col), _TEXT_CAST_TYPE).ilike(
210
238
  "%" + value + "%"
211
239
  )
212
240
 
@@ -487,9 +515,7 @@ class FilterGlobal(BaseFilterTextContains):
487
515
  filters = []
488
516
 
489
517
  for val in value:
490
- # Chain || instead of concat(*args): some databases cap function-call args,
491
- # which wide tables exceed. coalesce keeps concat's NULL-as-empty.
492
- concat_expr = None
518
+ concat_cols = []
493
519
  rel_filter_arr = []
494
520
  for col in cols:
495
521
  if (
@@ -499,13 +525,9 @@ class FilterGlobal(BaseFilterTextContains):
499
525
  ):
500
526
  rel_filter_arr.append(self._handle_text_contains(col, val))
501
527
  elif col in self.datamodel.list_columns:
502
- piece = sqlalchemy.func.coalesce(
503
- sqlalchemy.cast(self._cast_column(col), sa_types.String), ""
504
- )
505
- concat_expr = (
506
- piece if concat_expr is None else concat_expr + " " + piece
507
- )
528
+ concat_cols.append(self._cast_column(col))
508
529
  params = [*rel_filter_arr]
530
+ concat_expr = self._build_concat_expr(concat_cols)
509
531
  if concat_expr is not None:
510
532
  val = val.strip().lower()
511
533
  params.append(concat_expr.ilike("%" + val + "%"))
@@ -1,5 +1,6 @@
1
1
  import io
2
2
  import pathlib
3
+ import re
3
4
  import typing
4
5
  import zipfile
5
6
 
@@ -35,16 +36,26 @@ def callback(
35
36
 
36
37
  @app.command()
37
38
  def create_app(
39
+ name: typing.Annotated[
40
+ str,
41
+ typer.Option(
42
+ prompt="Project name",
43
+ help="The name of the new application. Fills the project-name placeholders in the generated files.",
44
+ ),
45
+ ],
38
46
  directory: typing.Annotated[
39
47
  str,
40
48
  typer.Option(
41
- ...,
42
49
  help="The directory where the new FastAPI RTK application will be created.",
43
50
  ),
44
51
  ] = ".",
45
52
  ):
46
53
  """
47
54
  Create a new FastAPI RTK application with a predefined structure from https://codeberg.org/datatactics/fastapi-rtk-skeleton.
55
+
56
+ Args:
57
+ name (str): The project name; normalized and substituted into the scaffolded project's placeholder files.
58
+ directory (str): Target directory for the new application. Defaults to the current directory.
48
59
  """
49
60
  with httpx.Client() as client:
50
61
  url = "https://codeberg.org/datatactics/fastapi-rtk-skeleton/archive/main.zip"
@@ -84,8 +95,41 @@ def create_app(
84
95
  with open(target_file, "wb") as f:
85
96
  f.write(source.read())
86
97
 
98
+ _apply_project_name(pathlib.Path(directory), name)
99
+
87
100
  typer.echo(f"FastAPI RTK application skeleton created in {directory}")
88
101
 
89
102
 
103
+ def _apply_project_name(directory: pathlib.Path, name: str) -> None:
104
+ """
105
+ Substitute the skeleton's project-name placeholders with the given name.
106
+
107
+ Each placeholder receives the casing its file expects: a slug for the
108
+ distribution name, upper snake for the config env var, and the raw name for
109
+ the page title. Missing files are skipped so partial scaffolds do not error.
110
+
111
+ Args:
112
+ directory (pathlib.Path): Root directory of the freshly created application.
113
+ name (str): Raw project name entered by the user.
114
+ """
115
+ raw = name.strip()
116
+ slug = re.sub(r"[-_.\s]+", "-", raw.lower()).strip("-")
117
+ env = slug.upper().replace("-", "_")
118
+ replacements = {
119
+ "pyproject.toml": [("project-name", slug)],
120
+ "requirements.txt": [("project-name", slug)],
121
+ "app/__init__.py": [("PROJECTNAME", env)],
122
+ "webapp/index.html": [("YOUR_APP_NAME_HERE", raw)],
123
+ }
124
+ for relative_path, subs in replacements.items():
125
+ target = directory / relative_path
126
+ if not target.exists():
127
+ continue
128
+ content = target.read_text(encoding="utf-8")
129
+ for placeholder, value in subs:
130
+ content = content.replace(placeholder, value)
131
+ target.write_text(content, encoding="utf-8")
132
+
133
+
90
134
  def main():
91
135
  app()
@@ -122,7 +122,9 @@ class Api(Model):
122
122
  id: Mapped[int] = mapped_column(
123
123
  sqlalchemy.Integer, sqlalchemy.Sequence(API_SEQUENCE), primary_key=True
124
124
  )
125
- name: Mapped[str] = mapped_column(sqlalchemy.String, unique=True, nullable=False)
125
+ name: Mapped[str] = mapped_column(
126
+ sqlalchemy.String(256), unique=True, nullable=False
127
+ )
126
128
 
127
129
  permissions: Mapped[list[PermissionApi]] = relationship(
128
130
  PermissionApi, back_populates="api", cascade="all, delete-orphan"
@@ -143,7 +145,9 @@ class Permission(Model):
143
145
  id: Mapped[int] = mapped_column(
144
146
  sqlalchemy.Integer, sqlalchemy.Sequence(PERMISSION_SEQUENCE), primary_key=True
145
147
  )
146
- name: Mapped[str] = mapped_column(sqlalchemy.String, unique=True, nullable=False)
148
+ name: Mapped[str] = mapped_column(
149
+ sqlalchemy.String(256), unique=True, nullable=False
150
+ )
147
151
 
148
152
  apis: Mapped[list[PermissionApi]] = relationship(
149
153
  PermissionApi, back_populates="permission", cascade="all, delete-orphan"
@@ -158,7 +162,9 @@ class Role(Model):
158
162
  id: Mapped[int] = mapped_column(
159
163
  sqlalchemy.Integer, sqlalchemy.Sequence(ROLE_SEQUENCE), primary_key=True
160
164
  )
161
- name: Mapped[str] = mapped_column(sqlalchemy.String, unique=True, nullable=False)
165
+ name: Mapped[str] = mapped_column(
166
+ sqlalchemy.String(256), unique=True, nullable=False
167
+ )
162
168
 
163
169
  users: Mapped[list["User"]] = relationship(
164
170
  "User", secondary=assoc_user_role, back_populates="roles"
@@ -1,7 +1,6 @@
1
1
  import datetime
2
2
 
3
3
  import pytest
4
-
5
4
  from fastapi_rtk.backends.generic.column import GenericColumn
6
5
  from fastapi_rtk.backends.generic.filters import (
7
6
  GenericBaseFilter,
@@ -109,6 +108,18 @@ class TestCastValue:
109
108
  f = GenericFilterEqual(StubInterface({"flag": bool}))
110
109
  assert f._cast_value("flag", 1) is True
111
110
 
111
+ def test_boolean_cast_native_true(self, caplog):
112
+ f = GenericFilterEqual(StubInterface({"flag": bool}))
113
+ with caplog.at_level("WARNING", logger="DT_FASTAPI"):
114
+ assert f._cast_value("flag", True) is True
115
+ assert not any("Failed to cast" in r.message for r in caplog.records)
116
+
117
+ def test_boolean_cast_native_false(self, caplog):
118
+ f = GenericFilterEqual(StubInterface({"flag": bool}))
119
+ with caplog.at_level("WARNING", logger="DT_FASTAPI"):
120
+ assert f._cast_value("flag", False) is False
121
+ assert not any("Failed to cast" in r.message for r in caplog.records)
122
+
112
123
  def test_date_cast(self):
113
124
  f = GenericFilterEqual(StubInterface({"d": datetime.date}))
114
125
  assert f._cast_value("d", "2024-01-15") == datetime.date(2024, 1, 15)
@@ -188,6 +188,18 @@ class TestCastValue:
188
188
  f = FilterEqual(iface)
189
189
  assert f._cast_value("active", "false") is False
190
190
 
191
+ def test_boolean_native_true(self, iface, caplog):
192
+ f = FilterEqual(iface)
193
+ with caplog.at_level("WARNING", logger="DT_FASTAPI"):
194
+ assert f._cast_value("active", True) is True
195
+ assert not caplog.records
196
+
197
+ def test_boolean_native_false(self, iface, caplog):
198
+ f = FilterEqual(iface)
199
+ with caplog.at_level("WARNING", logger="DT_FASTAPI"):
200
+ assert f._cast_value("active", False) is False
201
+ assert not caplog.records
202
+
191
203
 
192
204
  class TestSQLAFilterConverter:
193
205
  def test_conversion_table_present(self):
@@ -555,3 +567,51 @@ class TestSqlaFiltersCastValueExtras:
555
567
  out = f._cast_value("col", [1, 2, 3])
556
568
  # Recursive: list of casted values
557
569
  assert out == ["1", "2", "3"]
570
+
571
+
572
+ class TestOracleDialectCompilation:
573
+ def test_global_filter_casts_with_varchar2_length(self, session, WideRow_cls):
574
+ from sqlalchemy.dialects import oracle
575
+
576
+ from fastapi_rtk.backends.sqla.filters import FilterGlobal
577
+
578
+ iface = SQLAInterface(WideRow_cls)
579
+ f = FilterGlobal(iface)
580
+ stmt = f.apply(sqlalchemy.select(iface.obj), list(iface.list_columns), "needle")
581
+
582
+ compiled = str(stmt.compile(dialect=oracle.dialect()))
583
+ assert "AS VARCHAR2(4000 CHAR)" in compiled
584
+ assert "AS VARCHAR2)" not in compiled
585
+ assert "concat(" not in compiled.lower()
586
+
587
+ def test_text_contains_casts_with_varchar2_length(self, Person_cls):
588
+ from sqlalchemy.dialects import oracle
589
+
590
+ iface = SQLAInterface(Person_cls)
591
+ f = FilterTextContains(iface)
592
+ stmt = f.apply(sqlalchemy.select(iface.obj), "name", "alice")
593
+
594
+ compiled = str(stmt.compile(dialect=oracle.dialect()))
595
+ assert "AS VARCHAR2(4000 CHAR)" in compiled
596
+ assert "AS VARCHAR2)" not in compiled
597
+
598
+ def test_relation_contains_avoids_multi_arg_concat(self, Person_cls):
599
+ from sqlalchemy.dialects import oracle
600
+
601
+ iface = SQLAInterface(Person_cls)
602
+ f = FilterTextContains(iface)
603
+ stmt = f.apply(sqlalchemy.select(iface.obj), "country", "usa")
604
+
605
+ compiled = str(stmt.compile(dialect=oracle.dialect()))
606
+ assert "concat(" not in compiled.lower()
607
+ assert "AS VARCHAR2(4000 CHAR)" in compiled
608
+
609
+ def test_non_oracle_dialect_keeps_length_less_cast(self, Person_cls):
610
+ from sqlalchemy.dialects import postgresql
611
+
612
+ iface = SQLAInterface(Person_cls)
613
+ f = FilterTextContains(iface)
614
+ stmt = f.apply(sqlalchemy.select(iface.obj), "name", "alice")
615
+
616
+ compiled = str(stmt.compile(dialect=postgresql.dialect()))
617
+ assert "AS VARCHAR)" in compiled
@@ -0,0 +1,172 @@
1
+ """Tier 14 - tests for fastapi_rtk.cli.cli (top-level Typer app)."""
2
+
3
+ import io
4
+ import unittest.mock
5
+ import zipfile
6
+
7
+ import typer.testing
8
+ from fastapi_rtk.cli.cli import app, main
9
+ from fastapi_rtk.version import __version__
10
+
11
+ runner = typer.testing.CliRunner()
12
+
13
+
14
+ def _build_skeleton_zip(
15
+ *, with_naked_root_entry: bool = False, with_placeholders: bool = True
16
+ ) -> bytes:
17
+ """Synthetic skeleton archive that mimics the layout of the upstream zip."""
18
+ buf = io.BytesIO()
19
+ with zipfile.ZipFile(buf, "w") as zf:
20
+ if with_naked_root_entry:
21
+ # Triggers the empty-target_path continue branch
22
+ zf.writestr("skeleton-main", "")
23
+ zf.writestr("skeleton-main/", "")
24
+ zf.writestr("skeleton-main/README.md", "# skeleton")
25
+ if with_placeholders:
26
+ zf.writestr("skeleton-main/pyproject.toml", 'name = "project-name"\n')
27
+ zf.writestr(
28
+ "skeleton-main/requirements.txt",
29
+ "# via project-name (pyproject.toml)\n",
30
+ )
31
+ zf.writestr(
32
+ "skeleton-main/app/__init__.py", 'ENV_VAR = "PROJECTNAME_CONFIG"\n'
33
+ )
34
+ zf.writestr(
35
+ "skeleton-main/webapp/index.html", "<title>YOUR_APP_NAME_HERE</title>\n"
36
+ )
37
+ zf.writestr("skeleton-main/app/", "")
38
+ zf.writestr("skeleton-main/app/main.py", "print('ok')")
39
+ return buf.getvalue()
40
+
41
+
42
+ def _fake_client(zip_bytes: bytes):
43
+ """Build a drop-in httpx.Client replacement serving a fixed zip archive."""
44
+
45
+ class FakeResponse:
46
+ content = zip_bytes
47
+
48
+ def raise_for_status(self):
49
+ pass
50
+
51
+ class FakeClient:
52
+ def __enter__(self):
53
+ return self
54
+
55
+ def __exit__(self, *a):
56
+ pass
57
+
58
+ def get(self, url):
59
+ return FakeResponse()
60
+
61
+ return FakeClient
62
+
63
+
64
+ class TestHelp:
65
+ def test_help_returns_zero(self):
66
+ result = runner.invoke(app, ["--help"])
67
+ assert result.exit_code == 0
68
+
69
+ def test_help_shows_create_app(self):
70
+ result = runner.invoke(app, ["--help"])
71
+ assert "create-app" in result.output
72
+
73
+
74
+ class TestVersion:
75
+ def test_version_returns_zero(self):
76
+ result = runner.invoke(app, ["--version"])
77
+ assert result.exit_code == 0
78
+
79
+ def test_version_shows_version_string(self):
80
+ result = runner.invoke(app, ["--version"])
81
+ assert __version__ in result.output
82
+
83
+
84
+ class TestCreateAppHelp:
85
+ def test_create_app_help(self):
86
+ result = runner.invoke(app, ["create-app", "--help"])
87
+ assert result.exit_code == 0
88
+ assert "directory" in result.output.lower()
89
+
90
+ def test_create_app_help_shows_name(self):
91
+ result = runner.invoke(app, ["create-app", "--help"])
92
+ assert "name" in result.output.lower()
93
+
94
+
95
+ class TestCreateAppExtraction:
96
+ def test_create_app_extracts_skeleton(self, tmp_path):
97
+ zip_bytes = _build_skeleton_zip()
98
+ with unittest.mock.patch(
99
+ "fastapi_rtk.cli.cli.httpx.Client", _fake_client(zip_bytes)
100
+ ):
101
+ result = runner.invoke(
102
+ app, ["create-app", "--name", "demo", "--directory", str(tmp_path)]
103
+ )
104
+ assert result.exit_code == 0, result.output
105
+ assert (tmp_path / "README.md").exists()
106
+ assert (tmp_path / "app" / "main.py").exists()
107
+ assert "skeleton created" in result.output
108
+
109
+ def test_create_app_handles_naked_root_entry(self, tmp_path):
110
+ zip_bytes = _build_skeleton_zip(with_naked_root_entry=True)
111
+ with unittest.mock.patch(
112
+ "fastapi_rtk.cli.cli.httpx.Client", _fake_client(zip_bytes)
113
+ ):
114
+ result = runner.invoke(
115
+ app, ["create-app", "--name", "demo", "--directory", str(tmp_path)]
116
+ )
117
+ assert result.exit_code == 0, result.output
118
+ assert (tmp_path / "README.md").exists()
119
+
120
+
121
+ class TestCreateAppProjectName:
122
+ def test_create_app_substitutes_all_placeholders(self, tmp_path):
123
+ zip_bytes = _build_skeleton_zip()
124
+ with unittest.mock.patch(
125
+ "fastapi_rtk.cli.cli.httpx.Client", _fake_client(zip_bytes)
126
+ ):
127
+ result = runner.invoke(
128
+ app,
129
+ ["create-app", "--name", "My Cool App", "--directory", str(tmp_path)],
130
+ )
131
+ assert result.exit_code == 0, result.output
132
+ assert 'name = "my-cool-app"' in (tmp_path / "pyproject.toml").read_text()
133
+ assert "my-cool-app" in (tmp_path / "requirements.txt").read_text()
134
+ assert (
135
+ 'ENV_VAR = "MY_COOL_APP_CONFIG"'
136
+ in (tmp_path / "app" / "__init__.py").read_text()
137
+ )
138
+ assert (
139
+ "<title>My Cool App</title>"
140
+ in (tmp_path / "webapp" / "index.html").read_text()
141
+ )
142
+
143
+ def test_create_app_prompts_for_name(self, tmp_path):
144
+ zip_bytes = _build_skeleton_zip()
145
+ with unittest.mock.patch(
146
+ "fastapi_rtk.cli.cli.httpx.Client", _fake_client(zip_bytes)
147
+ ):
148
+ result = runner.invoke(
149
+ app,
150
+ ["create-app", "--directory", str(tmp_path)],
151
+ input="prompted-app\n",
152
+ )
153
+ assert result.exit_code == 0, result.output
154
+ assert 'name = "prompted-app"' in (tmp_path / "pyproject.toml").read_text()
155
+
156
+ def test_create_app_without_placeholders_is_noop(self, tmp_path):
157
+ zip_bytes = _build_skeleton_zip(with_placeholders=False)
158
+ with unittest.mock.patch(
159
+ "fastapi_rtk.cli.cli.httpx.Client", _fake_client(zip_bytes)
160
+ ):
161
+ result = runner.invoke(
162
+ app, ["create-app", "--name", "demo", "--directory", str(tmp_path)]
163
+ )
164
+ assert result.exit_code == 0, result.output
165
+ assert not (tmp_path / "pyproject.toml").exists()
166
+
167
+
168
+ class TestMain:
169
+ def test_main_invokes_app(self):
170
+ with unittest.mock.patch("fastapi_rtk.cli.cli.app") as mock_app:
171
+ main()
172
+ mock_app.assert_called_once()
@@ -250,3 +250,10 @@ class TestPermissionApiPersist:
250
250
  assert pa.id is not None
251
251
  assert pa.permission.name == "can_edit"
252
252
  assert pa.api.name == "WidgetApi"
253
+
254
+
255
+ class TestColumnLengths:
256
+ def test_name_columns_have_explicit_length(self):
257
+ # Oracle rejects a length-less VARCHAR2 in DDL (ORA-00906).
258
+ for model in (Api, Permission, Role):
259
+ assert model.__table__.c.name.type.length == 256
@@ -1 +0,0 @@
1
- __version__ = "2.10.2"
@@ -1,117 +0,0 @@
1
- """Tier 14 - tests for fastapi_rtk.cli.cli (top-level Typer app)."""
2
-
3
- import io
4
- import unittest.mock
5
- import zipfile
6
-
7
- import typer.testing
8
-
9
- from fastapi_rtk.cli.cli import app, main
10
- from fastapi_rtk.version import __version__
11
-
12
- runner = typer.testing.CliRunner()
13
-
14
-
15
- def _build_skeleton_zip(*, with_naked_root_entry: bool = False) -> bytes:
16
- """Synthetic skeleton archive that mimics the layout of the upstream zip."""
17
- buf = io.BytesIO()
18
- with zipfile.ZipFile(buf, "w") as zf:
19
- if with_naked_root_entry:
20
- # Triggers the empty-target_path continue branch
21
- zf.writestr("skeleton-main", "")
22
- zf.writestr("skeleton-main/", "")
23
- zf.writestr("skeleton-main/README.md", "# skeleton")
24
- zf.writestr("skeleton-main/app/", "")
25
- zf.writestr("skeleton-main/app/main.py", "print('ok')")
26
- return buf.getvalue()
27
-
28
-
29
- class TestHelp:
30
- def test_help_returns_zero(self):
31
- result = runner.invoke(app, ["--help"])
32
- assert result.exit_code == 0
33
-
34
- def test_help_shows_create_app(self):
35
- result = runner.invoke(app, ["--help"])
36
- assert "create-app" in result.output
37
-
38
-
39
- class TestVersion:
40
- def test_version_returns_zero(self):
41
- result = runner.invoke(app, ["--version"])
42
- assert result.exit_code == 0
43
-
44
- def test_version_shows_version_string(self):
45
- result = runner.invoke(app, ["--version"])
46
- assert __version__ in result.output
47
-
48
-
49
- class TestCreateAppHelp:
50
- def test_create_app_help(self):
51
- result = runner.invoke(app, ["create-app", "--help"])
52
- assert result.exit_code == 0
53
- assert "directory" in result.output.lower()
54
-
55
-
56
- class TestCreateAppExtraction:
57
- def test_create_app_extracts_skeleton(self, tmp_path):
58
- # Mock httpx.Client to return a fake zip archive so the command
59
- # exercises its extraction logic without hitting the network.
60
- zip_bytes = _build_skeleton_zip()
61
-
62
- class FakeResponse:
63
- content = zip_bytes
64
-
65
- def raise_for_status(self):
66
- pass
67
-
68
- class FakeClient:
69
- def __enter__(self):
70
- return self
71
-
72
- def __exit__(self, *a):
73
- pass
74
-
75
- def get(self, url):
76
- return FakeResponse()
77
-
78
- with unittest.mock.patch("fastapi_rtk.cli.cli.httpx.Client", FakeClient):
79
- result = runner.invoke(app, ["create-app", "--directory", str(tmp_path)])
80
- assert result.exit_code == 0, result.output
81
- assert (tmp_path / "README.md").exists()
82
- assert (tmp_path / "app" / "main.py").exists()
83
- assert "skeleton created" in result.output
84
-
85
- def test_create_app_handles_naked_root_entry(self, tmp_path):
86
- # Some zip producers list the top-level dir without a trailing slash;
87
- # exercise the empty-target_path skip branch.
88
- zip_bytes = _build_skeleton_zip(with_naked_root_entry=True)
89
-
90
- class FakeResponse:
91
- content = zip_bytes
92
-
93
- def raise_for_status(self):
94
- pass
95
-
96
- class FakeClient:
97
- def __enter__(self):
98
- return self
99
-
100
- def __exit__(self, *a):
101
- pass
102
-
103
- def get(self, url):
104
- return FakeResponse()
105
-
106
- with unittest.mock.patch("fastapi_rtk.cli.cli.httpx.Client", FakeClient):
107
- result = runner.invoke(app, ["create-app", "--directory", str(tmp_path)])
108
- assert result.exit_code == 0, result.output
109
- assert (tmp_path / "README.md").exists()
110
-
111
-
112
- class TestMain:
113
- def test_main_invokes_app(self):
114
- # main() calls app() under typer; verify it's a callable that runs.
115
- with unittest.mock.patch("fastapi_rtk.cli.cli.app") as mock_app:
116
- main()
117
- mock_app.assert_called_once()
File without changes
File without changes
File without changes