fastapi-rtk 2.19.1__tar.gz → 2.20.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/PKG-INFO +1 -1
  2. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/__init__.py +9 -0
  3. fastapi_rtk-2.20.0/fastapi_rtk/_version.py +1 -0
  4. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/agent_docs/guide/backend.md +38 -2
  5. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/agent_docs/guide/frontend.md +7 -3
  6. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/__init__.py +3 -0
  7. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/__init__.py +3 -0
  8. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/model_rest_api.py +131 -7
  9. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/params.py +20 -3
  10. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/__init__.py +2 -0
  11. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_info_service.py +70 -10
  12. fastapi_rtk-2.20.0/fastapi_rtk/api/model_rest_api/services/_lookup_service.py +460 -0
  13. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/generic/__init__.py +2 -0
  14. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/generic/db.py +3 -3
  15. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/generic/filters.py +29 -4
  16. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/generic/interface.py +41 -2
  17. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/generic/session.py +23 -3
  18. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/__init__.py +2 -0
  19. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/db.py +3 -3
  20. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/filters.py +28 -3
  21. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/interface.py +32 -2
  22. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/db.py +81 -15
  23. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/interface.py +69 -3
  24. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/const.py +4 -0
  25. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/lang/messages.pot +27 -1
  26. fastapi_rtk-2.20.0/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  27. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +28 -2
  28. fastapi_rtk-2.20.0/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  29. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +28 -2
  30. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/schemas.py +117 -3
  31. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/_user_service.py +1 -1
  32. fastapi_rtk-2.20.0/tests/integration/api/test_lookup.py +742 -0
  33. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/api/test_model_rest_api.py +38 -8
  34. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/api/model_rest_api/test_init.py +24 -10
  35. fastapi_rtk-2.20.0/tests/unit/api/model_rest_api/test_lookup_service.py +280 -0
  36. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/api/model_rest_api/test_service_delegators.py +4 -0
  37. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/generic/test_db.py +4 -4
  38. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/generic/test_filters.py +18 -0
  39. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/generic/test_interface.py +56 -2
  40. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/generic/test_session.py +13 -6
  41. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_filters.py +29 -19
  42. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_interface.py +61 -3
  43. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/bases/test_db.py +64 -7
  44. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/bases/test_interface.py +24 -3
  45. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_const.py +1 -0
  46. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_schemas.py +43 -0
  47. fastapi_rtk-2.19.1/fastapi_rtk/_version.py +0 -1
  48. fastapi_rtk-2.19.1/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  49. fastapi_rtk-2.19.1/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  50. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/.gitignore +0 -0
  51. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/LICENSE +0 -0
  52. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/README.md +0 -0
  53. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/agent_docs/adapters/agents-md/AGENTS.md +0 -0
  54. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/agent_docs/adapters/claude/skill.frontmatter +0 -0
  55. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/agent_docs/guide/jsonforms.md +0 -0
  56. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/agent_docs/guide/legacy.md +0 -0
  57. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/agent_docs/guide/overview.md +0 -0
  58. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/base_api.py +0 -0
  59. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_add_service.py +0 -0
  60. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_bulk_service.py +0 -0
  61. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_delete_service.py +0 -0
  62. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_download_service.py +0 -0
  63. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_edit_service.py +0 -0
  64. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_file_service.py +0 -0
  65. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_list_service.py +0 -0
  66. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_schema_service.py +0 -0
  67. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/api/model_rest_api/services/_show_service.py +0 -0
  68. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/apis/__init__.py +0 -0
  69. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/apis/info.py +0 -0
  70. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/apis/license.py +0 -0
  71. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/__init__.py +0 -0
  72. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/auth.py +0 -0
  73. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/hashers/__init__.py +0 -0
  74. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
  75. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
  76. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/ldap.py +0 -0
  77. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
  78. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
  79. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/strategies/__init__.py +0 -0
  80. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/strategies/config.py +0 -0
  81. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/strategies/db.py +0 -0
  82. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/auth/strategies/jwt.py +0 -0
  83. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/__init__.py +0 -0
  84. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/generic/column.py +0 -0
  85. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/generic/exceptions.py +0 -0
  86. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/generic/model.py +0 -0
  87. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/column.py +0 -0
  88. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
  89. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
  90. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
  91. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_service.py +0 -0
  92. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_service.py +0 -0
  93. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
  94. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/audit/_processing_service.py +0 -0
  95. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
  96. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
  97. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
  98. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
  99. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
  100. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/model.py +0 -0
  101. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/backends/sqla/session.py +0 -0
  102. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/__init__.py +0 -0
  103. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/cache.py +0 -0
  104. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/file_manager.py +0 -0
  105. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/filter.py +0 -0
  106. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/loop_manager.py +0 -0
  107. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/model.py +0 -0
  108. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/session.py +0 -0
  109. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/bases/user_cache.py +0 -0
  110. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/caches/__init__.py +0 -0
  111. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/caches/cache.py +0 -0
  112. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/caches/serializers.py +0 -0
  113. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/caches/storages.py +0 -0
  114. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/__init__.py +0 -0
  115. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/cli.py +0 -0
  116. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/__init__.py +0 -0
  117. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
  118. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/agent/__init__.py +0 -0
  119. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/agent/adapters/__init__.py +0 -0
  120. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/agent/adapters/agents_md.py +0 -0
  121. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/agent/adapters/claude.py +0 -0
  122. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/agent/base.py +0 -0
  123. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/agent/docs.py +0 -0
  124. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
  125. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
  126. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
  127. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
  128. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
  129. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
  130. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
  131. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
  132. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
  133. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/export.py +0 -0
  134. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/security.py +0 -0
  135. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/commands/translate.py +0 -0
  136. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/const.py +0 -0
  137. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/decorators.py +0 -0
  138. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/types.py +0 -0
  139. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/cli/utils.py +0 -0
  140. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/config.py +0 -0
  141. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/db.py +0 -0
  142. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/decorators.py +0 -0
  143. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/dependencies.py +0 -0
  144. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/exceptions.py +0 -0
  145. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
  146. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/file_managers/__init__.py +0 -0
  147. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/file_managers/file_manager.py +0 -0
  148. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/file_managers/image_manager.py +0 -0
  149. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
  150. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
  151. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/filters.py +0 -0
  152. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/globals.py +0 -0
  153. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/lang/__init__.py +0 -0
  154. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/lang/babel/__init__.py +0 -0
  155. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/lang/babel/cli.py +0 -0
  156. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/lang/babel/config.py +0 -0
  157. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/lang/babel.cfg +0 -0
  158. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/lang/lazy_text.py +0 -0
  159. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/loop_managers/__init__.py +0 -0
  160. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/loop_managers/loop_manager.py +0 -0
  161. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/manager.py +0 -0
  162. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/middlewares.py +0 -0
  163. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/mixins.py +0 -0
  164. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/models.py +0 -0
  165. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/py.typed +0 -0
  166. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/registry.py +0 -0
  167. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/routers.py +0 -0
  168. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/__init__.py +0 -0
  169. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/__init__.py +0 -0
  170. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/apis.py +0 -0
  171. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/models.py +0 -0
  172. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
  173. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/_builtin_role_service.py +0 -0
  174. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/_entities.py +0 -0
  175. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
  176. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
  177. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
  178. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/_permission_service.py +0 -0
  179. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/_results.py +0 -0
  180. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/_role_service.py +0 -0
  181. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/security_manager/security_manager.py +0 -0
  182. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/security/sqla/user_cache.py +0 -0
  183. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/setting.py +0 -0
  184. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/types.py +0 -0
  185. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/__init__.py +0 -0
  186. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/async_task_runner.py +0 -0
  187. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/class_factory.py +0 -0
  188. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/csv_json_converter.py +0 -0
  189. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/deep_merge.py +0 -0
  190. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/extender_mixin.py +0 -0
  191. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
  192. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/formatter.py +0 -0
  193. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/hooks.py +0 -0
  194. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/lazy.py +0 -0
  195. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/merge_schema.py +0 -0
  196. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
  197. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/pydantic.py +0 -0
  198. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/run_utils.py +0 -0
  199. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/self_dependencies.py +0 -0
  200. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
  201. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/sqla.py +0 -0
  202. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/timezone.py +0 -0
  203. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/update_signature.py +0 -0
  204. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/utils/use_default_when_none.py +0 -0
  205. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/fastapi_rtk/version.py +0 -0
  206. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/pyproject.toml +0 -0
  207. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/conftest.py +0 -0
  208. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/docker-compose.gis.yml +0 -0
  209. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/__init__.py +0 -0
  210. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/api/__init__.py +0 -0
  211. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/api/test_add_edit_relation_response.py +0 -0
  212. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/apis/__init__.py +0 -0
  213. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/apis/test_info_callable_schema.py +0 -0
  214. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/apis/test_info_contract.py +0 -0
  215. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/auth/__init__.py +0 -0
  216. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/auth/test_auth_flow.py +0 -0
  217. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/auth/test_oauth.py +0 -0
  218. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/conftest.py +0 -0
  219. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/file_managers/__init__.py +0 -0
  220. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/file_managers/test_file_routes.py +0 -0
  221. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/security/__init__.py +0 -0
  222. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/security/sqla/__init__.py +0 -0
  223. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/security/sqla/conftest.py +0 -0
  224. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/security/sqla/test_permission_service.py +0 -0
  225. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/security/sqla/test_role_service.py +0 -0
  226. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/security/sqla/test_security_manager.py +0 -0
  227. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/security/sqla/test_user_service.py +0 -0
  228. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/integration/test_app_smoke.py +0 -0
  229. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/__init__.py +0 -0
  230. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/api/__init__.py +0 -0
  231. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/api/conftest.py +0 -0
  232. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/api/model_rest_api/__init__.py +0 -0
  233. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/api/model_rest_api/test_columns.py +0 -0
  234. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/api/model_rest_api/test_download_service.py +0 -0
  235. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
  236. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/api/test_base_api.py +0 -0
  237. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/apis/__init__.py +0 -0
  238. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/apis/test_info.py +0 -0
  239. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/apis/test_license.py +0 -0
  240. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/__init__.py +0 -0
  241. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/hashers/__init__.py +0 -0
  242. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
  243. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/hashers/test_scrypt.py +0 -0
  244. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/password_helpers/__init__.py +0 -0
  245. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/password_helpers/test_fab.py +0 -0
  246. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/strategies/__init__.py +0 -0
  247. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/strategies/test_config.py +0 -0
  248. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/strategies/test_db.py +0 -0
  249. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/strategies/test_jwt.py +0 -0
  250. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/auth/test_auth.py +0 -0
  251. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/__init__.py +0 -0
  252. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/generic/__init__.py +0 -0
  253. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/generic/test_column.py +0 -0
  254. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/generic/test_exceptions.py +0 -0
  255. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/generic/test_model.py +0 -0
  256. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/__init__.py +0 -0
  257. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/conftest.py +0 -0
  258. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_audit.py +0 -0
  259. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_audit_service_delegators.py +0 -0
  260. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_audit_types.py +0 -0
  261. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_column.py +0 -0
  262. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_db.py +0 -0
  263. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
  264. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_model.py +0 -0
  265. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/backends/sqla/test_session.py +0 -0
  266. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/bases/__init__.py +0 -0
  267. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/bases/test_file_manager.py +0 -0
  268. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/bases/test_filter.py +0 -0
  269. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/bases/test_model.py +0 -0
  270. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/bases/test_session.py +0 -0
  271. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/__init__.py +0 -0
  272. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/commands/__init__.py +0 -0
  273. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/commands/conftest.py +0 -0
  274. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/commands/test_db.py +0 -0
  275. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/commands/test_export.py +0 -0
  276. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/commands/test_security.py +0 -0
  277. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/commands/test_security_crypto.py +0 -0
  278. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/commands/test_translate.py +0 -0
  279. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/conftest.py +0 -0
  280. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/test_cli.py +0 -0
  281. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/test_decorators.py +0 -0
  282. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/test_types.py +0 -0
  283. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/cli/test_utils.py +0 -0
  284. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/file_managers/__init__.py +0 -0
  285. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/file_managers/conftest.py +0 -0
  286. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/file_managers/test_file_manager.py +0 -0
  287. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/file_managers/test_image_manager.py +0 -0
  288. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
  289. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
  290. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/lang/__init__.py +0 -0
  291. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/lang/test_babel_cli.py +0 -0
  292. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/lang/test_babel_config.py +0 -0
  293. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/lang/test_lazy_text.py +0 -0
  294. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/__init__.py +0 -0
  295. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/__init__.py +0 -0
  296. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/conftest.py +0 -0
  297. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/security_manager/__init__.py +0 -0
  298. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/security_manager/test_builtin_role_service.py +0 -0
  299. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/security_manager/test_entities.py +0 -0
  300. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/security_manager/test_permission_service.py +0 -0
  301. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/security_manager/test_results.py +0 -0
  302. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/security_manager/test_role_service.py +0 -0
  303. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/security_manager/test_user_service.py +0 -0
  304. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/test_apis.py +0 -0
  305. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/test_models.py +0 -0
  306. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/security/sqla/test_security_manager.py +0 -0
  307. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_caches.py +0 -0
  308. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_config.py +0 -0
  309. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_db.py +0 -0
  310. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_decorators.py +0 -0
  311. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_dependencies.py +0 -0
  312. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_exceptions.py +0 -0
  313. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_fastapi_react_toolkit.py +0 -0
  314. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_filters.py +0 -0
  315. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_globals.py +0 -0
  316. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_manager.py +0 -0
  317. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_middlewares.py +0 -0
  318. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_mixins.py +0 -0
  319. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_models.py +0 -0
  320. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_registry.py +0 -0
  321. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_routers.py +0 -0
  322. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_setting.py +0 -0
  323. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_types.py +0 -0
  324. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_user_cache.py +0 -0
  325. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/test_version.py +0 -0
  326. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/__init__.py +0 -0
  327. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_async_task_runner.py +0 -0
  328. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_class_factory.py +0 -0
  329. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_csv_json_converter.py +0 -0
  330. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_deep_merge.py +0 -0
  331. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_extender_mixin.py +0 -0
  332. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
  333. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_formatter.py +0 -0
  334. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_hooks.py +0 -0
  335. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_lazy.py +0 -0
  336. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_merge_schema.py +0 -0
  337. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
  338. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_pydantic.py +0 -0
  339. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_run_utils.py +0 -0
  340. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_self_dependencies.py +0 -0
  341. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_smartdefaultdict.py +0 -0
  342. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_sqla.py +0 -0
  343. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_timezone.py +0 -0
  344. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_update_signature.py +0 -0
  345. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/tests/unit/utils/test_use_default_when_none.py +0 -0
  346. {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fastapi-rtk
3
- Version: 2.19.1
3
+ Version: 2.20.0
4
4
  Summary: A package that provides a set of tools to build a FastAPI application with a Class-Based CRUD API.
5
5
  Project-URL: Homepage, https://codeberg.org/datatactics/fastapi-rtk
6
6
  Project-URL: Issues, https://codeberg.org/datatactics/fastapi-rtk/issues
@@ -41,12 +41,15 @@ __all__ = [
41
41
  "P_ID",
42
42
  "P_IDS",
43
43
  "P_Q",
44
+ "P_LOOKUP_Q",
45
+ "P_COLUMN",
44
46
  "P_SESSION",
45
47
  "P_BODY",
46
48
  "P_ITEM",
47
49
  "P_ITEMS",
48
50
  "PARAM_Q",
49
51
  "PARAM_IDS_Q_SESSION_ITEMS",
52
+ "PARAM_LOOKUP_Q_COLUMN_SESSION",
50
53
  "PARAM_ID_SESSION",
51
54
  "PARAM_ID_SESSION_ITEM",
52
55
  "PARAM_BODY_SESSION",
@@ -87,6 +90,8 @@ __all__ = [
87
90
  "GenericFilterGreaterEqual",
88
91
  "GenericFilterSmallerEqual",
89
92
  "GenericFilterIn",
93
+ "GenericFilterSearch",
94
+ # TODO: Remove when the deprecated global_filter alias is dropped
90
95
  "GenericFilterGlobal",
91
96
  "GenericBaseOprFilter",
92
97
  "GenericFilterConverter",
@@ -139,6 +144,8 @@ __all__ = [
139
144
  "FilterRelationOneToOneOrManyToOneNotEqual",
140
145
  "FilterRelationOneToManyOrManyToManyIn",
141
146
  "FilterRelationOneToManyOrManyToManyNotIn",
147
+ "FilterSearch",
148
+ # TODO: Remove when the deprecated global_filter alias is dropped
142
149
  "FilterGlobal",
143
150
  "BaseOprFilter",
144
151
  "SQLAFilterConverter",
@@ -248,6 +255,7 @@ __all__ = [
248
255
  "ColumnRelationInfo",
249
256
  "SearchFilter",
250
257
  "InfoResponse",
258
+ "LookupResponse",
251
259
  "BaseResponse",
252
260
  "BaseResponseSingle",
253
261
  "BaseResponseMany",
@@ -255,6 +263,7 @@ __all__ = [
255
263
  "FilterSchema",
256
264
  "QuerySchema",
257
265
  "QueryBody",
266
+ "LookupQuerySchema",
258
267
  # .security
259
268
  "Api",
260
269
  "Permission",
@@ -0,0 +1 @@
1
+ __version__ = "2.20.0"
@@ -150,6 +150,7 @@ Registers a full CRUD surface:
150
150
  - `PUT /items/{id}`: update
151
151
  - `DELETE /items/{id}`: delete
152
152
  - `GET /items/_info`: metadata (columns, filters, permissions), which the frontend uses
153
+ - `GET /items/_lookup/{column}?mode=add|edit|search`: paged values of one column (see "Column lookup" in §8)
153
154
  - `GET /items/download`: CSV export
154
155
  - `POST /items/bulk/{handler}`: bulk operations (see §12)
155
156
  - `GET /items/_file/{filename}` / `_image/{filename}`: served when the model has file/image columns
@@ -245,6 +246,8 @@ class ItemApi(ModelRestApi):
245
246
  async def post_delete(self, item: Item, params): ...
246
247
  ```
247
248
 
249
+ The read routes have response hooks that receive the response body before it is returned and edit it in place: `pre_info(info, permissions, session)`, `pre_get_list(body, params)`, `pre_get(body, params)` and `pre_lookup(body, params)` (`params.q` is the `LookupQuerySchema`, plus `params.column` and `params.session`).
250
+
248
251
  To reject a request from a pre-hook, `raise fastapi.HTTPException(...)` (a bare `raise ValueError(...)` becomes HTTP 500; there is no auto-translation). To override the response without rejecting, return a non-`Model` value from the hook, and the dispatcher treats that value as the response and skips the rest of the pipeline (see `services/_add_service.py:66-72` and `services/_edit_service.py:90-96`).
249
252
 
250
253
  Use hooks for normalization, cross-field validation, audit logging, cache invalidation: anything policy, not wire format (use Pydantic for that at the schema level).
@@ -292,6 +295,39 @@ More per-Api knobs:
292
295
  - `order_columns` / `search_columns`: restrict which columns are sortable / searchable.
293
296
  - `add_jsonforms_schema` / `edit_jsonforms_schema`, `..._uischema`, `..._translations`, `..._use_enum`, `..._jsonforms_list_as_object`: override the JsonForms schema/uischema/translations that `/_info` ships to the frontend add/edit dialogs (frontend reference §9 is the consumer).
294
297
 
298
+ ### Column lookup
299
+
300
+ `/_info` ships only the first `max_page_size` options of a relation; `GET /items/_lookup/{column}` searches and pages the values of one column instead. Query params, bound as one `LookupQuerySchema` (per-Api `lookup_query_schema`): `mode` (required: `add`, `edit` or `search`; any other value is 422), `search` (text the values must contain, matched by `FilterSearch` like the list route's `search` param), `page` (zero-based, default 0), `page_size` (default and cap `max_page_size`), `ids` (a JSON list of primary keys to resolve, default `[]`). The response is `LookupResponse`, `{"count": int, "values": [...]}`: `{"id", "value"}` entries for a relation (`value` is `str(item)`), distinct non-null scalars in ascending order for a plain column. The route is dropped from `routes` when no column is lookup-enabled in any mode.
301
+
302
+ - Query validation (400, before access): `ids` that is not JSON (`json_invalid`) or not a list (`list_type`), a negative `page` (`greater_than_equal`), a `page_size` below 1 (`greater_than`).
303
+ - Gating order: 401 (route protected, no user), 404 `COLUMN_NOT_FOUND` (column not lookup-enabled in that mode), 403 (user lacks `can_post` for `add`, `can_put` for `edit`, `can_get` for `search`). There is no `can_lookup` permission; `"lookup"` in `exclude_protected_routes` skips the 401 and 403 checks.
304
+ - After access, without a custom `query`: `ids` on a plain column is 400 `invalid_key` (ids are for relations), and an id that does not parse as the related primary key is 400 `value_error` at `ids[<index>]`.
305
+ - `pre_lookup(self, body: LookupResponse, params)` runs on every response before it is returned (custom `query` included); `params` is `PARAM_LOOKUP_Q_COLUMN_SESSION` with `q` (the `LookupQuerySchema`), `column` and `session`. Edit `body` in place.
306
+ - Lookup is opt-in: a column is lookup-enabled only when its `lookup_columns` entry has `"enabled": True` (a bare `True` / `False` entry is short for `{"enabled": True}` / `{"enabled": False}`), so an Api without `lookup_columns` has no `lookup` route. An enabled relation is looked up in each mode whose `add_columns` / `edit_columns` / `search_columns` holds it, and needs something to search: the entry's `columns` or `query`, or a related model `__repr_columns__` that names at least one column, relation or dotted path (`search` matches those entries; a `@property` entry is skipped). An enabled plain column is looked up only in `search` mode, from `search_columns`. A dotted column and a composite-pk relation are never lookup-enabled.
307
+ - A relation lookup applies that mode's `*_query_rel_fields` and orders by `order_rel_fields[column]`, else ascending on the first search column that is a plain column of the related model; a plain-column lookup applies `base_filters` and `base_opr_filters`.
308
+ - `ids` lookups apply that mode's `*_query_rel_fields` too and take any number of ids in one request: a saved value those filters now exclude comes back without its row, so it gets no label.
309
+
310
+ ```python
311
+ class ItemApi(ModelRestApi):
312
+ datamodel = SQLAInterface(Item)
313
+ lookup_columns = {
314
+ "city": True, # plain column, search mode; short for {"enabled": True}
315
+ "supplier": {"enabled": True}, # searches the related __repr_columns__
316
+ "category": {"enabled": True, "columns": ["name", "code"]}, # columns search matches
317
+ "owner": {"enabled": True, "query": my_query}, # (api, column, q, session) -> {"count": int, "values": list}
318
+ }
319
+
320
+ async def pre_lookup(self, body, params): # every lookup response
321
+ if params.column == "city":
322
+ body.values = [v.title() for v in body.values]
323
+ ```
324
+
325
+ A `query` callable (sync or async) receives `q`, the request's `LookupQuerySchema`: `q.mode` (`"add"`, `"edit"` or `"search"`), `q.search` (search text or `None`), `q.page` (zero-based), `q.page_size` (already capped at `max_page_size`) and `q.ids` (a list of primary key strings, empty when not resolving). When `q.ids` is not empty, a relation's `query` must return exactly the rows with those primary keys.
326
+
327
+ An invalid entry raises `ValueError` at setup: a dotted key, a key that is not a column or relation, a `columns` name that matches nothing on the related model, an enabled relation with nothing to search, a relation in none of `add_columns` / `edit_columns` / `search_columns`, a plain column outside `search_columns` or of a JSON / binary / file / image / list kind.
328
+
329
+ `/_info` tells the frontend which columns to look up. A relation in `add_schema` gets `properties[col]["x-lookup"] = {"mode": "add"}` (`edit_schema` likewise with `{"mode": "edit"}`) when it is lookup-enabled for that mode, the requester may call the route in that mode (the 401 / 403 rules above), and the first page holds fewer values than exist. On a to-many relation the marker sits on the array property, not on `items`. The first-page `oneOf` / `enum` stays beside the marker, so an older frontend keeps working. Every search column carries `filters[col]["lookup"]`, a bool following the same rules for `search` mode; a lookup-enabled plain column needs no count check. An anonymous caller of a public `_info` on a protected `lookup` route gets no marker and `false` everywhere. A custom `add_jsonforms_schema` / `edit_jsonforms_schema` is served without the marker.
330
+
295
331
  ## 9. Filters and custom filters
296
332
 
297
333
  Available filter classes (import from `fastapi_rtk`, whose top level re-exports `backends.sqla.filters`; the legacy `fastapi_rtk.filters` module still works but emits a deprecation warning):
@@ -300,12 +336,12 @@ Available filter classes (import from `fastapi_rtk`, whose top level re-exports
300
336
  FilterEqual, FilterNotEqual, FilterGreater, FilterSmaller, FilterGreaterEqual, FilterSmallerEqual,
301
337
  FilterStartsWith, FilterNotStartsWith, FilterEndsWith, FilterNotEndsWith,
302
338
  FilterContains, FilterNotContains, FilterTextContains,
303
- FilterIn, FilterBetween, FilterGlobal,
339
+ FilterIn, FilterBetween, FilterSearch,
304
340
  FilterRelationOneToOneOrManyToOneEqual, FilterRelationOneToOneOrManyToOneNotEqual,
305
341
  FilterRelationOneToManyOrManyToManyIn, FilterRelationOneToManyOrManyToManyNotIn,
306
342
  ```
307
343
 
308
- (`FilterGlobal`, arg `global`, backs the frontend's global search box.)
344
+ (`FilterSearch` is not a column operator: it applies the list route's `search` query param across the shown columns, and an interface swaps it through `search_filter_class`. The old names `global_filter` (query param and `DBQueryParams` key), `FilterGlobal`, `global_filter_class` and `apply_global_filter` still work but emit a `DeprecationWarning`.)
309
345
 
310
346
  Apply as always-on filters on the Api class:
311
347
 
@@ -59,7 +59,7 @@ Other `Provider` props: `langProps` (i18n extension, §12), `adapters` (§14), `
59
59
  Key props:
60
60
 
61
61
  - `resource_name`: **must match** the backend's `ModelRestApi.resource_name`. If the backend says `items`, this says `items`: not `item`, not `/items`, not `Items`. This is the contract; no magic fixes a mismatch.
62
- - `initialQueryParams`: seeds pagination, sort, and filters. Use a small, stable object; don't reconstruct inline each render. Full `QueryParams` keys: `page`, `page_size`, `order_column`, `order_direction`, `filters` (list of `{col, opr, value}`), `opr_filters`, `global_filter`, `columns`, `with_count`.
62
+ - `initialQueryParams`: seeds pagination, sort, and filters. Use a small, stable object; don't reconstruct inline each render. Full `QueryParams` keys: `page`, `page_size`, `order_column`, `order_direction`, `filters` (list of `{col, opr, value}`), `opr_filters`, `search`, `columns`, `with_count` (`global_filter` is the deprecated name of `search`).
63
63
  - `streaming`: switch the provider to infinite-scroll mode (§11).
64
64
  - Behavior knobs: `relation`, `fetchInfo`, `clearInfoOnRefetch`, `clearDataOnRefetch`, `refetchInfoBasedOnLang`, `refetchDataBasedOnLang`, `resetQueryParamsOnPathChange`, plus raw TanStack overrides `infoQueryProps` / `dataQueryProps` / `streamingQueryProps`. `withApiProvider` is the HOC variant.
65
65
 
@@ -70,7 +70,8 @@ Filter operators are the backend filter classes' `arg_name` strings (defined in
70
70
  - string: `sw` (starts with), `nsw` (not), `ew` (ends with), `new` (not), `ct` (contains), `nct` (not), `tc` (text-contains, case-insensitive)
71
71
  - set / range: `in`, `bw` (between)
72
72
  - relations: `rel_o_m` / `nrel_o_m` (one-to-many), `rel_m_m` / `nrel_m_m` (many-to-many)
73
- - global search: `global`
73
+
74
+ Text search across the shown columns is the `search` query param, not an operator.
74
75
 
75
76
  ## 3. `useApi`: the CRUD hook
76
77
 
@@ -116,6 +117,8 @@ function ItemsPage() {
116
117
 
117
118
  There is no `execute(...)` on `useApi`; for arbitrary requests see §4.
118
119
 
120
+ `lookup(column, { mode, search?, page?, page_size?, ids? })` fetches one page of the backend's `/_lookup/{column}` route and resolves to `{ count, values }`: `{ id, value }` rows for a relation, scalars for a plain column. `mode` is `add`, `edit` or `search`; `ids` resolves saved primary keys to labels instead of searching, all in one request. A server-searched input is a plain Mantine `Select` or `MultiSelect` with the props of `useLookupOptions({ column, lookup, mode, relation?, values, infiniteQueryProps? })` (`fastapi-rtk/hooks`) spread after its own, `lookup` usually being `useApi().lookup`: it calls `lookup` when the dropdown opens and after typing pauses (`DEBOUNCE_LOOKUP_DELAY`, 200 ms), appends the next page each time the dropdown is scrolled to the bottom (until `count` values arrived or a page is empty, then closes a list that took more than one page with a disabled, translated `No more results` option), resolves a selected relation id missing from the loaded values through `ids` when it passes the mode's `*_query_rel_fields` (a plain value is its own label), and returns `data`, `loading` (true from the keystroke until the request finishes), `filter`, `onSearchChange`, `onDropdownOpen`, `onDropdownClose` and `scrollAreaProps`. Without a `lookup` or a `column` it fetches nothing. Every reopening starts again from the first page, each hook instance runs its own requests, and by default nothing is cached and a failed request is not retried; `infiniteQueryProps` (TanStack `useInfiniteQuery` options) is spread over those defaults of the search query. It runs on TanStack Query, so it (and `HeaderSelectInput` / `HeaderMultiSelectInput`, which always call it) needs a `QueryClientProvider` ancestor, which `Provider` supplies.
121
+
119
122
  Use `setQueryParams` rather than constructing URLs manually; it keeps the filter shape aligned with the backend's `/_info` contract.
120
123
 
121
124
  Also on the hook: `upload(file, options?)` (CSV import against the resource; `delimiter` / `quotechar` options), `getFileURL` / `getImageURL` (build URLs for file/image columns), `refetchInfo({force?})`, `path`, `queryParams`, and the raw TanStack queries `infoQuery` / `dataQuery` / `streamingQuery` as escape hatches.
@@ -255,7 +258,8 @@ import { NextGenDataGrid } from "fastapi-rtk";
255
258
  Key props:
256
259
 
257
260
  - Columns are auto-generated from `/_info` + `list_columns`; there is NO `columns` prop. Per-column overrides go in `columnProps`: a dict keyed by column name whose values are partial MRT `ColumnDef` objects (or factories).
258
- - Row identity comes from the list response's top-level `ids` array, never from a column. `useProvideApi` maps the payload to `{...item, id: data.ids[index]}` (`api/src/ApiProvider/hooks/useProvideApi.ts:183-186`, plus the streaming twin at `:236-239`), and `getRowId` reads `getItemId` = `item.id_ ?? item.id`. The zipped `id` is spread last, so it also overrides a same-named column. So adding `"id"` to the backend `list_columns` gives the grid nothing it does not already have, and only surfaces a raw `id` column to the user; `list_obj_schema` is generated `with_id=False`, which is why the raw JSON lacks one and a backend test reading `row["id"]` fails. Such a test should read the response's `ids` array instead of widening the projection.
261
+ - Row identity comes from the list response's top-level `ids` array, never from a column. `useProvideApi` maps the payload to `{...item, id: data.ids[index]}` (`api/src/ApiProvider/hooks/useProvideApi.ts:185-188`, plus the streaming twin at `:238-241`), and `getRowId` reads `getItemId` = `item.id_ ?? item.id`. The zipped `id` is spread last, so it also overrides a same-named column. So adding `"id"` to the backend `list_columns` gives the grid nothing it does not already have, and only surfaces a raw `id` column to the user; `list_obj_schema` is generated `with_id=False`, which is why the raw JSON lacks one and a backend test reading `row["id"]` fails. Such a test should read the response's `ids` array instead of widening the projection.
262
+ - Header filters follow `/_info`: a column whose `filters[col].lookup` is `true` (a relation with more rows than `_info` sent, or a plain column enabled in `lookup_columns`) searches the server through `HeaderSelectInput` for `eq` / `neq` / `rel_o_m` / `nrel_o_m` and `HeaderMultiSelectInput` for `in` / `rel_m_m` / `nrel_m_m`, both given a `lookup={{ column, relation }}` prop; every other operator keeps its regular input.
259
263
  - `quickFilters`: one-click filter presets rendered above the grid (`quickFiltersComponent` swaps the renderer).
260
264
  - `enableAdvancedFilters`: shows the structured filter builder.
261
265
  - `queryOnlyVisibleColumns`: perf, only requests visible columns from the backend.
@@ -9,12 +9,15 @@ __all__ = [
9
9
  "P_ID",
10
10
  "P_IDS",
11
11
  "P_Q",
12
+ "P_LOOKUP_Q",
13
+ "P_COLUMN",
12
14
  "P_SESSION",
13
15
  "P_BODY",
14
16
  "P_ITEM",
15
17
  "P_ITEMS",
16
18
  "PARAM_Q",
17
19
  "PARAM_IDS_Q_SESSION_ITEMS",
20
+ "PARAM_LOOKUP_Q_COLUMN_SESSION",
18
21
  "PARAM_ID_SESSION",
19
22
  "PARAM_ID_SESSION_ITEM",
20
23
  "PARAM_BODY_SESSION",
@@ -7,12 +7,15 @@ __all__ = [
7
7
  "P_ID",
8
8
  "P_IDS",
9
9
  "P_Q",
10
+ "P_LOOKUP_Q",
11
+ "P_COLUMN",
10
12
  "P_SESSION",
11
13
  "P_BODY",
12
14
  "P_ITEM",
13
15
  "P_ITEMS",
14
16
  "PARAM_Q",
15
17
  "PARAM_IDS_Q_SESSION_ITEMS",
18
+ "PARAM_LOOKUP_Q_COLUMN_SESSION",
16
19
  "PARAM_ID_SESSION",
17
20
  "PARAM_ID_SESSION_ITEM",
18
21
  "PARAM_BODY_SESSION",
@@ -9,6 +9,7 @@ plus one shared column / schema / body service:
9
9
 
10
10
  - ``image`` / ``file`` routes + ``pre_image`` / ``pre_file`` live on ``ModelRestApi``, backed by :class:`~.services.FileService` (``file_service``).
11
11
  - ``info`` route + ``pre_info`` + ``_generate_info_schema`` live on ``ModelRestApi``, backed by :class:`~.services.InfoService` (``info_service``).
12
+ - ``lookup`` route + ``pre_lookup`` live on ``ModelRestApi``, backed by :class:`~.services.LookupService` (``lookup_service``), which also owns the ``is_enabled`` and ``can_call`` rules ``_info`` reads.
12
13
  - ``get_list`` route + ``pre_get_list`` live on ``ModelRestApi``, backed by :class:`~.services.ListService` (``list_service``).
13
14
  - ``get`` route + ``pre_get`` live on ``ModelRestApi``, backed by :class:`~.services.ShowService` (``show_service``).
14
15
  - ``post`` route + ``pre_add`` / ``post_add`` live on ``ModelRestApi``, backed by :class:`~.services.AddService` (``add_service``).
@@ -49,7 +50,7 @@ from ...backends.sqla.interface import SQLAInterface
49
50
  from ...backends.sqla.model import Model
50
51
  from ...backends.sqla.session import SQLASession
51
52
  from ...bases import AbstractFileManager, AbstractInterface, DBQueryParams
52
- from ...const import AVAILABLE_ROUTES
53
+ from ...const import AVAILABLE_ROUTES, LookupMode
53
54
  from ...decorators import expose, permission_name, priority, protect
54
55
  from ...dependencies import current_permissions
55
56
  from ...exceptions import HTTPWithValidationException, raise_exception
@@ -62,6 +63,8 @@ from ...schemas import (
62
63
  ColumnRelationInfo,
63
64
  GeneralResponse,
64
65
  InfoResponse,
66
+ LookupQuerySchema,
67
+ LookupResponse,
65
68
  QueryBody,
66
69
  QuerySchema,
67
70
  )
@@ -73,6 +76,7 @@ from .params import (
73
76
  PARAM_ID_SESSION,
74
77
  PARAM_ID_SESSION_ITEM,
75
78
  PARAM_IDS_Q_SESSION_ITEMS,
79
+ PARAM_LOOKUP_Q_COLUMN_SESSION,
76
80
  )
77
81
  from .services import (
78
82
  AddService,
@@ -83,9 +87,11 @@ from .services import (
83
87
  FileService,
84
88
  InfoService,
85
89
  ListService,
90
+ LookupService,
86
91
  SchemaService,
87
92
  ShowService,
88
93
  )
94
+ from .services._lookup_service import LookupColumn
89
95
 
90
96
  if typing.TYPE_CHECKING:
91
97
  from .services._download_service import DownloadPageSizeResolver
@@ -108,7 +114,7 @@ class ModelRestApi(BaseApi):
108
114
  ``ModelRestApi`` is a facade over one lazily constructed ``*_service``
109
115
  instance per route family (``add_service``, ``edit_service``,
110
116
  ``delete_service``, ``bulk_service``, ``list_service``, ``show_service``,
111
- ``info_service``, ``download_service``, ``file_service``), plus
117
+ ``info_service``, ``lookup_service``, ``download_service``, ``file_service``), plus
112
118
  ``schema_service`` for the shared column / schema / body helpers.
113
119
  Config attributes, hooks, route registration, and delegator methods stay
114
120
  on this class; a route's logic lives on its service, reachable via the
@@ -182,6 +188,7 @@ class ModelRestApi(BaseApi):
182
188
  "image",
183
189
  "file",
184
190
  "info",
191
+ "lookup",
185
192
  "download",
186
193
  "bulk",
187
194
  "get_list",
@@ -194,13 +201,13 @@ class ModelRestApi(BaseApi):
194
201
  ]
195
202
  )
196
203
  """
197
- The list of routes to expose. Available routes: `info`, `download`, `bulk`, `get_list`, `get`, `post`, `put`, `delete`.
204
+ The list of routes to expose. Available routes: `info`, `lookup`, `download`, `bulk`, `get_list`, `get`, `post`, `put`, `delete`.
198
205
  """
199
206
  exclude_routes: list[AVAILABLE_ROUTES] = lazy(
200
207
  list[AVAILABLE_ROUTES], with_target=False
201
208
  )
202
209
  """
203
- The list of routes to exclude. Available routes: `info`, `download`, `bulk`, `get_list`, `get`, `post`, `put`, `delete`.
210
+ The list of routes to exclude. Available routes: `info`, `lookup`, `download`, `bulk`, `get_list`, `get`, `post`, `put`, `delete`.
204
211
  """
205
212
  protected_routes: list[AVAILABLE_ROUTES] = lazy(
206
213
  lambda self: [x for x in self.routes if x not in self.exclude_protected_routes]
@@ -331,6 +338,19 @@ class ModelRestApi(BaseApi):
331
338
  """
332
339
  The body schema for the download endpoint.
333
340
 
341
+ DO NOT MODIFY UNLESS YOU KNOW WHAT YOU ARE DOING.
342
+ """
343
+ lookup_query_schema: type[LookupQuerySchema] = lazy(
344
+ lambda self: merge_schema(
345
+ LookupQuerySchema,
346
+ {"page_size": (int | None, pydantic.Field(default=self.max_page_size))},
347
+ True,
348
+ f"{self.__class__.__name__}-LookupQuerySchema",
349
+ )
350
+ )
351
+ """
352
+ The query schema for the lookup endpoint.
353
+
334
354
  DO NOT MODIFY UNLESS YOU KNOW WHAT YOU ARE DOING.
335
355
  """
336
356
 
@@ -429,6 +449,24 @@ class ModelRestApi(BaseApi):
429
449
  }
430
450
  ```
431
451
  """
452
+ lookup_columns: dict[str, bool | LookupColumn] = lazy(
453
+ dict[str, bool | LookupColumn], with_target=False
454
+ )
455
+ """
456
+ Configures the `/_lookup/{column}` route per column. A bare bool is short for `{"enabled": <bool>}`.
457
+
458
+ - `enabled`: `True` turns the lookup on; every column defaults to off. Plain columns only from search columns, relations only when the related model has `__repr_columns__` or `columns` is set.
459
+ - `columns`: the related columns `search` matches, instead of `__repr_columns__`.
460
+ - `query`: replaces the lookup with `(api, column, q: LookupQuerySchema, session) -> {"count", "values"}`; with `q.ids` set it returns exactly those rows.
461
+
462
+ Example:
463
+ ```python
464
+ lookup_columns = {
465
+ "city": True,
466
+ "owner": {"enabled": True, "columns": ["first_name", "last_name"]},
467
+ }
468
+ ```
469
+ """
432
470
  search_filters: dict[str, list[type[BaseFilter]]] = lazy(
433
471
  lambda: dict[str, list[type[BaseFilter]]]()
434
472
  )
@@ -1104,6 +1142,10 @@ class ModelRestApi(BaseApi):
1104
1142
  """
1105
1143
  The service backing the info route's ``info`` / ``generate_info_schema`` implementations.
1106
1144
  """
1145
+ lookup_service: LookupService = lazy(lambda self: LookupService(self))
1146
+ """
1147
+ The service backing the lookup route's ``lookup`` implementation and the ``is_enabled`` and ``can_call`` rules the info route reads.
1148
+ """
1107
1149
  download_service: DownloadService = lazy(lambda self: DownloadService(self))
1108
1150
  """
1109
1151
  The service backing the download route's ``download`` / ``download_page_size`` / ``export_data`` / ``import_data`` implementations.
@@ -1286,6 +1328,8 @@ class ModelRestApi(BaseApi):
1286
1328
  },
1287
1329
  )
1288
1330
 
1331
+ self.lookup_service.validate_config()
1332
+
1289
1333
  def _init_routes(self):
1290
1334
  """
1291
1335
  Init routes for the API.
@@ -1295,6 +1339,12 @@ class ModelRestApi(BaseApi):
1295
1339
  unavailable.append("image")
1296
1340
  if not self.datamodel.get_file_column_list():
1297
1341
  unavailable.append("file")
1342
+ if not any(
1343
+ self.lookup_service.is_enabled(col, mode)
1344
+ for mode in typing.get_args(LookupMode)
1345
+ for col in [*self.add_columns, *self.edit_columns, *self.search_columns]
1346
+ ):
1347
+ unavailable.append("lookup")
1298
1348
 
1299
1349
  # Rebind on the instance instead of removing in place: `routes` may be a
1300
1350
  # class attribute shared with subclasses.
@@ -1342,6 +1392,21 @@ class ModelRestApi(BaseApi):
1342
1392
 
1343
1393
  return dependency
1344
1394
 
1395
+ def get_lookup_q_factory(self):
1396
+ """
1397
+ Returns a dependency that yields the lookup route's LookupQuerySchema.
1398
+
1399
+ Returns:
1400
+ collections.abc.Callable[..., Coroutine[typing.Any, typing.Any, LookupQuerySchema]]: The dependency that yields the query schema.
1401
+ """
1402
+
1403
+ async def dependency(
1404
+ q: typing.Annotated[self.lookup_query_schema, fastapi.Query()], # type: ignore
1405
+ ):
1406
+ return typing.cast(LookupQuerySchema, q)
1407
+
1408
+ return dependency
1409
+
1345
1410
  """
1346
1411
  --------------------------------------------------------------------------------------------------------
1347
1412
  ROUTE HELPER METHODS
@@ -1406,9 +1471,7 @@ class ModelRestApi(BaseApi):
1406
1471
  filter_classes=self.base_filters,
1407
1472
  opr_filters=q.opr_filters,
1408
1473
  opr_filter_classes=self.base_opr_filters,
1409
- global_filter=(q.columns or self.list_columns, q.global_filter)
1410
- if q.global_filter
1411
- else None,
1474
+ search=(q.columns or self.list_columns, q.search) if q.search else None,
1412
1475
  )
1413
1476
 
1414
1477
  """
@@ -1492,6 +1555,67 @@ class ModelRestApi(BaseApi):
1492
1555
  permissions, session, session_count
1493
1556
  )
1494
1557
 
1558
+ """
1559
+ -------------
1560
+ LOOKUP
1561
+ -------------
1562
+ """
1563
+
1564
+ def lookup(self):
1565
+ """
1566
+ Lookup endpoint for the API.
1567
+ """
1568
+ expose_params = {
1569
+ "name": "Lookup Column Values",
1570
+ "description": f"Search the values of a column of the model {self.datamodel.obj.__name__}: related rows by their display columns, or the distinct values of a plain column.",
1571
+ "response_model": LookupResponse | typing.Any,
1572
+ **self.expose_params.get("lookup", {}),
1573
+ }
1574
+ priority(7)(self.lookup_headless)
1575
+ expose("/_lookup/{column}", methods=["GET"], **expose_params)(
1576
+ self.lookup_headless
1577
+ )
1578
+
1579
+ async def lookup_headless(
1580
+ self,
1581
+ column: str,
1582
+ q: LookupQuerySchema = SelfDepends().get_lookup_q_factory,
1583
+ permissions: list[str] = SelfDepends().get_current_permissions,
1584
+ session: SQLASession = SelfDepends().datamodel.get_session_factory,
1585
+ session_count: SQLASession = SelfDepends().datamodel.get_session_factory,
1586
+ ):
1587
+ """
1588
+ Retrieves one page of lookup values in headless mode.
1589
+
1590
+ Args:
1591
+ column (str): The column to look up.
1592
+ q (LookupQuerySchema): The query schema. Its `page_size` is capped at `max_page_size`.
1593
+ permissions (list[str]): The current user's permissions on this API.
1594
+ session (SQLASession): The session for the values query.
1595
+ session_count (SQLASession): The session for the count query.
1596
+
1597
+ Returns:
1598
+ LookupResponse: The matching values and their total count.
1599
+
1600
+ ### Note:
1601
+ If you are overriding this method, make sure to copy all the decorators too.
1602
+ """
1603
+ return await self.lookup_service.lookup(
1604
+ column, q, permissions, session, session_count
1605
+ )
1606
+
1607
+ async def pre_lookup(
1608
+ self, body: LookupResponse, params: PARAM_LOOKUP_Q_COLUMN_SESSION
1609
+ ):
1610
+ """
1611
+ Pre-process the lookup response before returning it.
1612
+ The response still needs to adhere to the LookupResponse schema.
1613
+
1614
+ Args:
1615
+ body (LookupResponse): The response body.
1616
+ params (Params): Additional data passed to the handler.
1617
+ """
1618
+
1495
1619
  """
1496
1620
  -------------
1497
1621
  LIST
@@ -2,8 +2,9 @@
2
2
  Parameter dataclasses passed to ``ModelRestApi`` pre/post hooks.
3
3
 
4
4
  The plain ``Params`` base is fully optional; the ``P_*`` mixins narrow a single
5
- field to a non-optional type, and the ``PARAM_*`` aliases are the concrete
6
- combinations used by each route handler.
5
+ field to a non-optional type (``P_LOOKUP_Q`` types ``q`` as the lookup route's
6
+ query instead), and the ``PARAM_*`` aliases are the concrete combinations used
7
+ by each route handler.
7
8
  """
8
9
 
9
10
  import typing
@@ -13,19 +14,22 @@ import pydantic
13
14
  from ...backends.sqla.model import Model
14
15
  from ...backends.sqla.session import SQLASession
15
16
  from ...bases import AbstractSession
16
- from ...schemas import PRIMARY_KEY, QuerySchema
17
+ from ...schemas import PRIMARY_KEY, LookupQuerySchema, QuerySchema
17
18
 
18
19
  __all__ = [
19
20
  "Params",
20
21
  "P_ID",
21
22
  "P_IDS",
22
23
  "P_Q",
24
+ "P_LOOKUP_Q",
25
+ "P_COLUMN",
23
26
  "P_SESSION",
24
27
  "P_BODY",
25
28
  "P_ITEM",
26
29
  "P_ITEMS",
27
30
  "PARAM_Q",
28
31
  "PARAM_IDS_Q_SESSION_ITEMS",
32
+ "PARAM_LOOKUP_Q_COLUMN_SESSION",
29
33
  "PARAM_ID_SESSION",
30
34
  "PARAM_ID_SESSION_ITEM",
31
35
  "PARAM_BODY_SESSION",
@@ -37,6 +41,7 @@ class Params(pydantic.BaseModel):
37
41
  id: PRIMARY_KEY | None = None
38
42
  ids: list[PRIMARY_KEY] | None = None
39
43
  q: QuerySchema | None = None
44
+ column: str | None = None
40
45
  session: SQLASession | AbstractSession | typing.Any | None = None
41
46
  body: pydantic.BaseModel | None = None
42
47
  item: Model | typing.Any | None = None
@@ -56,6 +61,14 @@ class P_Q(Params):
56
61
  q: QuerySchema
57
62
 
58
63
 
64
+ class P_LOOKUP_Q(Params):
65
+ q: LookupQuerySchema
66
+
67
+
68
+ class P_COLUMN(Params):
69
+ column: str
70
+
71
+
59
72
  class P_SESSION(Params):
60
73
  session: SQLASession | AbstractSession | typing.Any
61
74
 
@@ -80,6 +93,10 @@ class PARAM_IDS_Q_SESSION_ITEMS(P_IDS, P_Q, P_SESSION, P_ITEMS):
80
93
  pass
81
94
 
82
95
 
96
+ class PARAM_LOOKUP_Q_COLUMN_SESSION(P_LOOKUP_Q, P_COLUMN, P_SESSION):
97
+ pass
98
+
99
+
83
100
  class PARAM_ID_SESSION(P_ID, P_SESSION):
84
101
  pass
85
102
 
@@ -8,6 +8,7 @@ from ._edit_service import EditService
8
8
  from ._file_service import FileService
9
9
  from ._info_service import InfoService
10
10
  from ._list_service import ListService
11
+ from ._lookup_service import LookupService
11
12
  from ._schema_service import SchemaService
12
13
  from ._show_service import ShowService
13
14
 
@@ -20,6 +21,7 @@ __all__ = [
20
21
  "FileService",
21
22
  "InfoService",
22
23
  "ListService",
24
+ "LookupService",
23
25
  "SchemaService",
24
26
  "ShowService",
25
27
  ]