fastapi-rtk 2.19.1__tar.gz → 2.20.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.
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/PKG-INFO +14 -4
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/README.md +13 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/__init__.py +9 -0
- fastapi_rtk-2.20.1/fastapi_rtk/_version.py +1 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/agent_docs/guide/backend.md +38 -2
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/agent_docs/guide/frontend.md +10 -6
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/__init__.py +3 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/__init__.py +3 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/model_rest_api.py +131 -7
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/params.py +20 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/__init__.py +2 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_info_service.py +70 -10
- fastapi_rtk-2.20.1/fastapi_rtk/api/model_rest_api/services/_lookup_service.py +460 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/generic/__init__.py +2 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/generic/db.py +3 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/generic/filters.py +29 -4
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/generic/interface.py +41 -2
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/generic/session.py +23 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/__init__.py +2 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/db.py +3 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/filters.py +28 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/interface.py +32 -2
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/db.py +81 -15
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/interface.py +69 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/const.py +4 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/lang/messages.pot +27 -1
- fastapi_rtk-2.20.1/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +28 -2
- fastapi_rtk-2.20.1/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +28 -2
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/schemas.py +117 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/_user_service.py +1 -1
- fastapi_rtk-2.20.1/tests/integration/api/test_lookup.py +742 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/api/test_model_rest_api.py +38 -8
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/api/model_rest_api/test_init.py +24 -10
- fastapi_rtk-2.20.1/tests/unit/api/model_rest_api/test_lookup_service.py +280 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/api/model_rest_api/test_service_delegators.py +4 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/generic/test_db.py +4 -4
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/generic/test_filters.py +18 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/generic/test_interface.py +56 -2
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/generic/test_session.py +13 -6
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_filters.py +29 -19
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_interface.py +61 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/bases/test_db.py +64 -7
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/bases/test_interface.py +24 -3
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_const.py +1 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_schemas.py +43 -0
- fastapi_rtk-2.19.1/fastapi_rtk/_version.py +0 -1
- fastapi_rtk-2.19.1/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
- fastapi_rtk-2.19.1/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/.gitignore +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/LICENSE +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/agent_docs/adapters/agents-md/AGENTS.md +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/agent_docs/adapters/claude/skill.frontmatter +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/agent_docs/guide/jsonforms.md +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/agent_docs/guide/legacy.md +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/agent_docs/guide/overview.md +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/base_api.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_add_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_bulk_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_delete_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_download_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_edit_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_file_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_list_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_schema_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/api/model_rest_api/services/_show_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/apis/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/apis/info.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/apis/license.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/auth.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/hashers/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/ldap.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/strategies/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/strategies/config.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/strategies/db.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/auth/strategies/jwt.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/generic/column.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/generic/exceptions.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/generic/model.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/column.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/audit/_processing_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/model.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/backends/sqla/session.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/cache.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/file_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/filter.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/loop_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/model.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/session.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/bases/user_cache.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/caches/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/caches/cache.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/caches/serializers.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/caches/storages.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/cli.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/agent/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/agent/adapters/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/agent/adapters/agents_md.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/agent/adapters/claude.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/agent/base.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/agent/docs.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/export.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/security.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/commands/translate.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/const.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/decorators.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/types.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/cli/utils.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/config.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/db.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/decorators.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/dependencies.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/exceptions.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/file_managers/file_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/file_managers/image_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/filters.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/globals.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/lang/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/lang/babel/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/lang/babel/cli.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/lang/babel/config.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/lang/babel.cfg +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/lang/lazy_text.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/loop_managers/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/loop_managers/loop_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/middlewares.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/mixins.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/models.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/py.typed +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/registry.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/routers.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/apis.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/models.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/_builtin_role_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/_entities.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/_permission_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/_results.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/_role_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/security_manager/security_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/security/sqla/user_cache.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/setting.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/types.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/async_task_runner.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/class_factory.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/csv_json_converter.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/deep_merge.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/extender_mixin.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/formatter.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/hooks.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/lazy.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/merge_schema.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/pydantic.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/run_utils.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/self_dependencies.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/sqla.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/timezone.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/update_signature.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/utils/use_default_when_none.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/fastapi_rtk/version.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/pyproject.toml +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/conftest.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/docker-compose.gis.yml +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/api/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/api/test_add_edit_relation_response.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/apis/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/apis/test_info_callable_schema.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/apis/test_info_contract.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/auth/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/auth/test_auth_flow.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/auth/test_oauth.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/conftest.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/file_managers/test_file_routes.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/security/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/security/sqla/conftest.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/security/sqla/test_permission_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/security/sqla/test_role_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/security/sqla/test_security_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/security/sqla/test_user_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/integration/test_app_smoke.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/api/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/api/conftest.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/api/model_rest_api/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/api/model_rest_api/test_columns.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/api/model_rest_api/test_download_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/api/test_base_api.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/apis/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/apis/test_info.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/apis/test_license.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/hashers/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/hashers/test_scrypt.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/password_helpers/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/password_helpers/test_fab.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/strategies/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/strategies/test_config.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/strategies/test_db.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/strategies/test_jwt.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/auth/test_auth.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/generic/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/generic/test_column.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/generic/test_exceptions.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/generic/test_model.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/conftest.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_audit.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_audit_service_delegators.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_audit_types.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_column.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_db.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_model.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/backends/sqla/test_session.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/bases/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/bases/test_file_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/bases/test_filter.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/bases/test_model.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/bases/test_session.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/commands/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/commands/conftest.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/commands/test_db.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/commands/test_export.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/commands/test_security.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/commands/test_security_crypto.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/commands/test_translate.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/conftest.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/test_cli.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/test_decorators.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/test_types.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/cli/test_utils.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/file_managers/conftest.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/file_managers/test_file_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/file_managers/test_image_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/lang/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/lang/test_babel_cli.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/lang/test_babel_config.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/lang/test_lazy_text.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/conftest.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/security_manager/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/security_manager/test_builtin_role_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/security_manager/test_entities.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/security_manager/test_permission_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/security_manager/test_results.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/security_manager/test_role_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/security_manager/test_user_service.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/test_apis.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/test_models.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/security/sqla/test_security_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_caches.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_config.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_db.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_decorators.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_dependencies.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_exceptions.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_fastapi_react_toolkit.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_filters.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_globals.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_manager.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_middlewares.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_mixins.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_models.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_registry.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_routers.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_setting.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_types.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_user_cache.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/test_version.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/__init__.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_async_task_runner.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_class_factory.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_csv_json_converter.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_deep_merge.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_extender_mixin.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_formatter.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_hooks.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_lazy.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_merge_schema.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_pydantic.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_run_utils.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_self_dependencies.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_smartdefaultdict.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_sqla.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_timezone.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_update_signature.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/tests/unit/utils/test_use_default_when_none.py +0 -0
- {fastapi_rtk-2.19.1 → fastapi_rtk-2.20.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fastapi-rtk
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.20.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
|
|
@@ -119,7 +119,7 @@ project/
|
|
|
119
119
|
1. Install FastAPI React Toolkit:
|
|
120
120
|
|
|
121
121
|
```bash
|
|
122
|
-
pip install fastapi-rtk
|
|
122
|
+
pip install fastapi-rtk aiosqlite # aiosqlite: async driver for the sqlite URI below (asyncpg for Postgres)
|
|
123
123
|
mkdir -p app
|
|
124
124
|
touch run.py app/__init__.py app/app.py app/config.py app/models.py app/apis.py
|
|
125
125
|
```
|
|
@@ -231,6 +231,7 @@ project/
|
|
|
231
231
|
- `GET /api/v1/items/_image/{filename}` - Serve image files (If image column is present)
|
|
232
232
|
- `GET /api/v1/items/_file/{filename}` - Serve file downloads (If file column is present)
|
|
233
233
|
- `GET /api/v1/items/_info` - Get metadata about the model, including which columns can be added, edited, filtered, etc.
|
|
234
|
+
- `GET /api/v1/items/_lookup/{column}` - Search a column's values, or resolve ids to labels, for form and filter inputs (If the column is enabled in `lookup_columns`)
|
|
234
235
|
- `POST /api/v1/items/bulk/{handler}` - Bulk operations, if set on the API class
|
|
235
236
|
- `GET /api/v1/items/download` - Download items as CSV
|
|
236
237
|
- `GET /api/v1/items/` - List items
|
|
@@ -249,6 +250,14 @@ project/
|
|
|
249
250
|
|
|
250
251
|
2. Project files:
|
|
251
252
|
|
|
253
|
+
**webapp/.env**
|
|
254
|
+
|
|
255
|
+
The path the app is served under, with a trailing slash; `index.html` reads it as `%VITE_BASE_PATH%`.
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
VITE_BASE_PATH=/
|
|
259
|
+
```
|
|
260
|
+
|
|
252
261
|
**webapp/index.html**
|
|
253
262
|
|
|
254
263
|
```html
|
|
@@ -295,7 +304,7 @@ project/
|
|
|
295
304
|
|
|
296
305
|
createRoot(document.getElementById("root")).render(
|
|
297
306
|
<StrictMode>
|
|
298
|
-
<MantineProvider>
|
|
307
|
+
<MantineProvider getStyleNonce={() => window.nonce}>
|
|
299
308
|
<Provider baseUrl={BASE_PATH + "api/v1"}>
|
|
300
309
|
<BrowserRouter basename={BASE_PATH}>
|
|
301
310
|
<App />
|
|
@@ -326,8 +335,9 @@ import { createReactNativeAdapters } from "fastapi-rtk/react-native-adapters";
|
|
|
326
335
|
const rnAdapters = createReactNativeAdapters({
|
|
327
336
|
asyncStorage,
|
|
328
337
|
fileSystem,
|
|
329
|
-
sharing,
|
|
338
|
+
sharing, // optional
|
|
330
339
|
webBrowser,
|
|
340
|
+
oauthRedirectUrl: "myapp://oauth", // required for OAuth sign-in; one URL or a map per provider
|
|
331
341
|
});
|
|
332
342
|
|
|
333
343
|
<Provider baseUrl="https://api.example.com/api/v1" adapters={rnAdapters}>
|
|
@@ -60,7 +60,7 @@ project/
|
|
|
60
60
|
1. Install FastAPI React Toolkit:
|
|
61
61
|
|
|
62
62
|
```bash
|
|
63
|
-
pip install fastapi-rtk
|
|
63
|
+
pip install fastapi-rtk aiosqlite # aiosqlite: async driver for the sqlite URI below (asyncpg for Postgres)
|
|
64
64
|
mkdir -p app
|
|
65
65
|
touch run.py app/__init__.py app/app.py app/config.py app/models.py app/apis.py
|
|
66
66
|
```
|
|
@@ -172,6 +172,7 @@ project/
|
|
|
172
172
|
- `GET /api/v1/items/_image/{filename}` - Serve image files (If image column is present)
|
|
173
173
|
- `GET /api/v1/items/_file/{filename}` - Serve file downloads (If file column is present)
|
|
174
174
|
- `GET /api/v1/items/_info` - Get metadata about the model, including which columns can be added, edited, filtered, etc.
|
|
175
|
+
- `GET /api/v1/items/_lookup/{column}` - Search a column's values, or resolve ids to labels, for form and filter inputs (If the column is enabled in `lookup_columns`)
|
|
175
176
|
- `POST /api/v1/items/bulk/{handler}` - Bulk operations, if set on the API class
|
|
176
177
|
- `GET /api/v1/items/download` - Download items as CSV
|
|
177
178
|
- `GET /api/v1/items/` - List items
|
|
@@ -190,6 +191,14 @@ project/
|
|
|
190
191
|
|
|
191
192
|
2. Project files:
|
|
192
193
|
|
|
194
|
+
**webapp/.env**
|
|
195
|
+
|
|
196
|
+
The path the app is served under, with a trailing slash; `index.html` reads it as `%VITE_BASE_PATH%`.
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
VITE_BASE_PATH=/
|
|
200
|
+
```
|
|
201
|
+
|
|
193
202
|
**webapp/index.html**
|
|
194
203
|
|
|
195
204
|
```html
|
|
@@ -236,7 +245,7 @@ project/
|
|
|
236
245
|
|
|
237
246
|
createRoot(document.getElementById("root")).render(
|
|
238
247
|
<StrictMode>
|
|
239
|
-
<MantineProvider>
|
|
248
|
+
<MantineProvider getStyleNonce={() => window.nonce}>
|
|
240
249
|
<Provider baseUrl={BASE_PATH + "api/v1"}>
|
|
241
250
|
<BrowserRouter basename={BASE_PATH}>
|
|
242
251
|
<App />
|
|
@@ -267,8 +276,9 @@ import { createReactNativeAdapters } from "fastapi-rtk/react-native-adapters";
|
|
|
267
276
|
const rnAdapters = createReactNativeAdapters({
|
|
268
277
|
asyncStorage,
|
|
269
278
|
fileSystem,
|
|
270
|
-
sharing,
|
|
279
|
+
sharing, // optional
|
|
271
280
|
webBrowser,
|
|
281
|
+
oauthRedirectUrl: "myapp://oauth", // required for OAuth sign-in; one URL or a map per provider
|
|
272
282
|
});
|
|
273
283
|
|
|
274
284
|
<Provider baseUrl="https://api.example.com/api/v1" adapters={rnAdapters}>
|
|
@@ -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.1"
|
|
@@ -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,
|
|
339
|
+
FilterIn, FilterBetween, FilterSearch,
|
|
304
340
|
FilterRelationOneToOneOrManyToOneEqual, FilterRelationOneToOneOrManyToOneNotEqual,
|
|
305
341
|
FilterRelationOneToManyOrManyToManyIn, FilterRelationOneToManyOrManyToManyNotIn,
|
|
306
342
|
```
|
|
307
343
|
|
|
308
|
-
(`
|
|
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
|
|
|
@@ -10,7 +10,7 @@ The toolkit expects Mantine as the UI kit. Use `Stack`, `Group`, `Grid`, `Simple
|
|
|
10
10
|
pnpm add fastapi-rtk @mantine/core @mantine/dates @mantine/form @mantine/hooks dayjs react-router
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Peer deps: the four `@mantine/*` packages (`^9.0.0`), `react` / `react-dom` (`^19.2.3`), `dayjs` (`^1.11.0`) and `react-router` (`^7.5.3 || ^8.0.0`).
|
|
13
|
+
Peer deps: the four `@mantine/*` packages (`^9.0.0`), `react` / `react-dom` (`^19.2.3`), `dayjs` (`^1.11.0`) and `react-router` (`^7.5.3 || ^8.0.0`), plus the optional `@jsonforms/core` / `@jsonforms/react` (`^3.6.0`), needed only when importing from `fastapi-rtk/jsonforms` (every other subpath, its types included, works without them). `fastapi-rtk` has no other dependencies: TanStack Query, zustand, i18next, react-i18next, yup, lodash, clsx, dompurify, mantine-form-yup-resolver, mantine-react-table (patched) and the JsonForms copy behind the built-in forms are bundled inside it. An app's own install of any of these is a separate copy that never sees the toolkit's: `Provider` always creates its own `QueryClient`, and only `rq` / `i18nr` / `useTranslation` imported from `fastapi-rtk` reach the Provider's client and i18next instance. Mantine >= 9.4.1 requires fastapi-rtk >= 2.8.0; older releases loop forever in the grid's `useColumns` ("Maximum update depth exceeded").
|
|
14
14
|
|
|
15
15
|
**Provider order is load-bearing.** Mantine must wrap the fastapi-rtk `Provider`, and the stylesheet order matters: Mantine CSS first, fastapi-rtk CSS last (RTK overrides).
|
|
16
16
|
|
|
@@ -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`, `
|
|
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
|
-
|
|
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.
|
|
@@ -144,7 +147,7 @@ await addEntry({ name: "x" }); // POST /{resource}
|
|
|
144
147
|
await updateEntry(id, { name: "y" }); // PUT /{resource}/{id}
|
|
145
148
|
```
|
|
146
149
|
|
|
147
|
-
**Option B, use the TanStack Query namespace `rq`.** `fastapi-rtk` re-exports `@tanstack/react-query` as `rq` (`export * as rq from '
|
|
150
|
+
**Option B, use the TanStack Query namespace `rq`.** `fastapi-rtk` re-exports its bundled `@tanstack/react-query` as `rq` (`export * as rq from './rq'` in `externals/src/index.ts`, surfaced by `core/src/index.ts:94`; import it from there rather than from `@tanstack/react-query`, whose separate copy cannot see the Provider's `QueryClient`; the sibling `mrt` namespace re-exports the bundled `mantine-react-table` the same way, with long-form aliases `reactQuery` / `mantineReactTable` at `core/src/index.ts:96`). The same namespaces, plus `ld` (lodash), `zs` (zustand), `yup` and `i18nr` (react-i18next), are importable from `fastapi-rtk` or `fastapi-rtk/externals`; importing them from `fastapi-rtk/api`, `/utils`, `/zustand` or `/contexts` is deprecated. Wrap a `useApi` action (or any toolkit-provided async function) in `rq.useQuery` / `rq.useMutation` so the result participates in the shared query cache (invalidation, refetch-on-focus, devtools). Never call raw `fetch` from the `queryFn` / `mutationFn`, since that re-introduces the exact problems `useCustomFetch` has.
|
|
148
151
|
|
|
149
152
|
```jsx
|
|
150
153
|
import { useApi, rq } from "fastapi-rtk";
|
|
@@ -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:
|
|
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.
|
|
@@ -399,7 +403,7 @@ const { t } = useTranslation(); // or: translation.t
|
|
|
399
403
|
<LanguageSelector />;
|
|
400
404
|
```
|
|
401
405
|
|
|
402
|
-
`useLang` reads/writes the selected language to localStorage. The translation function `t(key)` comes from
|
|
406
|
+
`useLang` reads/writes the selected language to localStorage. The translation function `t(key)` comes from the `useTranslation()` exported by `fastapi-rtk` (the bundled react-i18next's, which the Provider wires to its i18next instance, as it does `i18nr.Trans`; the one imported from `react-i18next` itself is a separate copy that never sees it) or via the `translation` field on `useLang()`. Source strings double as the English keys; a German dictionary ships built-in. Extend or override through the `Provider`'s `langProps` prop: `langProps.getI18nProps.translations` takes `{locale: {key: value}}`, `langProps.i18n` accepts a full i18next instance, and `langProps.MRTLocalization` overrides the grid's Mantine React Table strings.
|
|
403
407
|
|
|
404
408
|
## 13. Standard routes: `UserMenu`
|
|
405
409
|
|
|
@@ -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
|
-
|
|
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
|