fastapi-rtk 2.18.3__tar.gz → 2.19.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.18.3 → fastapi_rtk-2.19.1}/PKG-INFO +1 -1
- fastapi_rtk-2.19.1/fastapi_rtk/_version.py +1 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/agent_docs/adapters/agents-md/AGENTS.md +4 -1
- fastapi_rtk-2.19.1/fastapi_rtk/agent_docs/adapters/claude/skill.frontmatter +2 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/agent_docs/guide/backend.md +4 -27
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/agent_docs/guide/frontend.md +12 -197
- fastapi_rtk-2.19.1/fastapi_rtk/agent_docs/guide/jsonforms.md +165 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/agent_docs/guide/legacy.md +3 -3
- fastapi_rtk-2.19.1/fastapi_rtk/agent_docs/guide/overview.md +50 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_service.py +2 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/utils.py +4 -2
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/const.py +6 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/routers.py +16 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/apis.py +17 -3
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/auth/test_oauth.py +93 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_audit.py +19 -2
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/test_utils.py +10 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_const.py +11 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_routers.py +85 -0
- fastapi_rtk-2.18.3/fastapi_rtk/_version.py +0 -1
- fastapi_rtk-2.18.3/fastapi_rtk/agent_docs/adapters/claude/skill.frontmatter +0 -2
- fastapi_rtk-2.18.3/fastapi_rtk/agent_docs/guide/overview.md +0 -106
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/.gitignore +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/LICENSE +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/README.md +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/base_api.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/model_rest_api.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/params.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_add_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_bulk_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_delete_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_download_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_edit_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_file_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_info_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_list_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_schema_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_show_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/apis/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/apis/info.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/apis/license.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/auth.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/hashers/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/ldap.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/strategies/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/strategies/config.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/strategies/db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/strategies/jwt.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/column.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/exceptions.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/filters.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/interface.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/model.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/session.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/column.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/_processing_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/filters.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/interface.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/model.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/session.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/cache.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/file_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/filter.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/interface.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/loop_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/model.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/session.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/user_cache.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/caches/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/caches/cache.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/caches/serializers.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/caches/storages.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/cli.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/adapters/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/adapters/agents_md.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/adapters/claude.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/base.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/docs.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/export.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/security.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/translate.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/const.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/decorators.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/types.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/config.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/decorators.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/dependencies.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/exceptions.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/file_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/image_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/filters.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/globals.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/babel/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/babel/cli.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/babel/config.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/babel.cfg +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/lazy_text.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/messages.pot +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/loop_managers/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/loop_managers/loop_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/middlewares.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/mixins.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/models.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/py.typed +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/registry.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/schemas.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/models.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_builtin_role_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_entities.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_permission_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_results.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_role_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_user_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/security_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/user_cache.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/setting.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/types.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/async_task_runner.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/class_factory.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/csv_json_converter.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/deep_merge.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/extender_mixin.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/formatter.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/hooks.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/lazy.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/merge_schema.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/pydantic.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/run_utils.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/self_dependencies.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/sqla.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/timezone.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/update_signature.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/use_default_when_none.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/version.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/pyproject.toml +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/conftest.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/docker-compose.gis.yml +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/api/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/api/test_add_edit_relation_response.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/api/test_model_rest_api.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/apis/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/apis/test_info_callable_schema.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/apis/test_info_contract.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/auth/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/auth/test_auth_flow.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/conftest.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/file_managers/test_file_routes.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/conftest.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/test_permission_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/test_role_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/test_security_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/test_user_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/test_app_smoke.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/conftest.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_columns.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_download_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_init.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_service_delegators.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/test_base_api.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/apis/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/apis/test_info.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/apis/test_license.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/hashers/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/hashers/test_scrypt.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/password_helpers/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/password_helpers/test_fab.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/strategies/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/strategies/test_config.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/strategies/test_db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/strategies/test_jwt.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/test_auth.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_column.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_exceptions.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_filters.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_interface.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_model.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_session.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/conftest.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_audit_service_delegators.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_audit_types.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_column.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_filters.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_interface.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_model.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_session.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_file_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_filter.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_interface.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_model.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_session.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/conftest.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_export.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_security.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_security_crypto.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_translate.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/conftest.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/test_cli.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/test_decorators.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/test_types.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/conftest.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/test_file_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/test_image_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/lang/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/lang/test_babel_cli.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/lang/test_babel_config.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/lang/test_lazy_text.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/conftest.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_builtin_role_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_entities.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_permission_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_results.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_role_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_user_service.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/test_apis.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/test_models.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/test_security_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_caches.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_config.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_db.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_decorators.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_dependencies.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_exceptions.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_fastapi_react_toolkit.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_filters.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_globals.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_manager.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_middlewares.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_mixins.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_models.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_registry.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_schemas.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_setting.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_types.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_user_cache.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_version.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/__init__.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_async_task_runner.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_class_factory.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_csv_json_converter.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_deep_merge.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_extender_mixin.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_formatter.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_hooks.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_lazy.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_merge_schema.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_pydantic.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_run_utils.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_self_dependencies.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_smartdefaultdict.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_sqla.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_timezone.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_update_signature.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_use_default_when_none.py +0 -0
- {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.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.19.1
|
|
4
4
|
Summary: A package that provides a set of tools to build a FastAPI application with a Class-Based CRUD API.
|
|
5
5
|
Project-URL: Homepage, https://codeberg.org/datatactics/fastapi-rtk
|
|
6
6
|
Project-URL: Issues, https://codeberg.org/datatactics/fastapi-rtk/issues
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.19.1"
|
{fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/agent_docs/adapters/agents-md/AGENTS.md
RENAMED
|
@@ -11,7 +11,10 @@ Detailed guidance ships with the package and was installed into this project:
|
|
|
11
11
|
- `docs/agents/fastapi-rtk/backend.md`: models, `ModelRestApi`, hooks, filters, permissions,
|
|
12
12
|
migrations, file uploads, bulk handlers, OAuth.
|
|
13
13
|
- `docs/agents/fastapi-rtk/frontend.md`: `Provider`, `ApiProvider`, `useApi`, `NextGenDataGrid`,
|
|
14
|
-
`useForms`, dialogs,
|
|
14
|
+
`useForms`, dialogs, i18n.
|
|
15
|
+
- `docs/agents/fastapi-rtk/jsonforms.md`: `JsonFormsWithCustomizer`, schema conditionals, per-control
|
|
16
|
+
props, custom inputs through the `customizer` slot. Open it only for nested or conditional
|
|
17
|
+
schemas; ordinary CRUD forms are covered by `frontend.md`.
|
|
15
18
|
- `docs/agents/fastapi-rtk/legacy.md`: workarounds for older releases, keyed by symptom. Open it only
|
|
16
19
|
when this project pins an older fastapi-rtk than the guide was verified against.
|
|
17
20
|
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
name: rtk-developer
|
|
2
|
+
description: Use when building, modifying or debugging anything in a fastapi-rtk project: Python models, ModelRestApi or BaseApi endpoints, pre/post hooks, permissions, migrations, file uploads, bulk operations, or the React side (Provider setup, ApiProvider, useApi, NextGenDataGrid, useForms, dialogs, JsonForms). Also on any mention of fastapi-rtk or "rtk backend" / "rtk frontend", and in any project importing from `fastapi_rtk` or the `fastapi-rtk` JS package, where it is preferred over plain FastAPI or React guidance.
|
|
@@ -2,31 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Deep reference for the Python side of fastapi-rtk: project setup, models, APIs, hooks, permissions, migrations, file uploads, bulk operations, async tasks. Load this when the user is working on Python code inside an `app/` folder or writing anything that touches `fastapi_rtk` imports.
|
|
4
4
|
|
|
5
|
-
## Table of contents
|
|
6
|
-
|
|
7
|
-
1. Install and project layout
|
|
8
|
-
2. Configuration
|
|
9
|
-
3. App initialization
|
|
10
|
-
4. Models
|
|
11
|
-
5. REST APIs: `ModelRestApi`
|
|
12
|
-
6. REST APIs: `BaseApi` for custom endpoints
|
|
13
|
-
7. Pre/post hooks
|
|
14
|
-
8. Column configuration (list / show / add / edit / extra)
|
|
15
|
-
9. Filters and custom filters
|
|
16
|
-
10. Relationship filtering
|
|
17
|
-
11. File and image uploads
|
|
18
|
-
12. Bulk operations
|
|
19
|
-
13. Background and deferred tasks
|
|
20
|
-
14. Permissions, roles, and auth
|
|
21
|
-
15. OAuth providers
|
|
22
|
-
16. Migrations and user management CLI
|
|
23
|
-
17. The `g` global context
|
|
24
|
-
18. Multi-database / binds
|
|
25
|
-
19. Extensions and other backends
|
|
26
|
-
20. Production checklist
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
5
|
## 1. Install and project layout
|
|
31
6
|
|
|
32
7
|
```bash
|
|
@@ -608,7 +583,7 @@ Top-level shape: `dict[role_name, list[[api_glob, permission_glob]]]`. Both fiel
|
|
|
608
583
|
|
|
609
584
|
### The admin role sits outside `ROLES`
|
|
610
585
|
|
|
611
|
-
`Setting.AUTH_ROLE_ADMIN` (`setting.py:
|
|
586
|
+
`Setting.AUTH_ROLE_ADMIN` (`setting.py:368`, default `DEFAULT_ADMIN_ROLE = "Admin"` from `const.py:71`) names a role that is deliberately absent from `ROLES`, reachable as `g.admin_role` (`globals.py:141-147`). It holds **every permission of every registered Api**, and a `ROLES` edit cannot narrow that: `_associate_permission_with_api` (`fastapi_react_toolkit.py:506`) writes one `PermissionApi` row per `(permission, api)` taken from each Api's exposed routes plus one per `ROLES` entry, then `_associate_role_with_permission_api` (`:545`) grants the admin role every one of those rows except the ones whose permission name contains `|` (`:566-576`). The per-route row for the same permission is a different row and is never excluded, so piping a `ROLES` entry to hide something from the admin role is a no-op. The cleanup pass also exempts that role from disassociation by name, so an existing grant row survives every restart.
|
|
612
587
|
|
|
613
588
|
Revoking a permission from it means setting `AUTH_ROLE_ADMIN` to `None` or to an existing `ROLES` role name (which strips the blanket grant entirely, since `if g.admin_role:` guards it), or checking inside the route.
|
|
614
589
|
|
|
@@ -696,10 +671,12 @@ OAUTH_PROVIDERS = [
|
|
|
696
671
|
OAUTH_REDIRECT_URI = "http://localhost:6006"
|
|
697
672
|
```
|
|
698
673
|
|
|
699
|
-
Top-level shape: `list[dict]`. Each dict's `"oauth_client"` is the `httpx_oauth` client; optional keys: `associate_by_email`, `redirect_url`, `redirect_url_factory`, `redirect_url_after_callback` (defaults to `OAUTH_REDIRECT_URI`), `on_before_register`, `on_after_register` (receives the freshly-created user), `on_before_login`. The setting key `OAUTH_CLIENTS` is the canonical name; `OAUTH_PROVIDERS` is accepted as an alternate (`setting.py:
|
|
674
|
+
Top-level shape: `list[dict]`. Each dict's `"oauth_client"` is the `httpx_oauth` client; optional keys: `backend` (`"cookie"` default, or `"jwt"`), `associate_by_email`, `redirect_url`, `redirect_url_factory`, `redirect_url_after_callback` (defaults to `OAUTH_REDIRECT_URI` on a cookie client), `on_before_register`, `on_after_register` (receives the freshly-created user), `on_before_login`. The setting key `OAUTH_CLIENTS` is the canonical name; `OAUTH_PROVIDERS` is accepted as an alternate (`setting.py:390-391`).
|
|
700
675
|
|
|
701
676
|
The toolkit wires the login/callback routes; the frontend's OAuth sign-in hook kicks off the flow.
|
|
702
677
|
|
|
678
|
+
A `"backend": "jwt"` client (for clients without a browser cookie store, such as React Native; web apps default to `cookie` and can opt in) answers the callback with `{"access_token", "token_type": "bearer"}` JSON instead of a cookie, so the client has to make the callback request itself: point `redirect_url` (or `redirect_url_factory`) at the URL the app receives the redirect on (on React Native the same URL goes to `createReactNativeAdapters({ oauthRedirectUrl })`, and `oauthSignin` then completes the sign-in itself), or call `oauthCallback("<name>", redirectQuery)` from `useAuth()` with that URL's query string, which forwards `code` and `state` to `GET /api/v1/auth/oauth-authorized/<name>` and stores the returned token. A JWT client rejects `redirect_url_after_callback` with a `ValueError` at startup and a popup login (`?popup=true`) with 400 `OAUTH_POPUP_NOT_SUPPORTED`, since both would drop the token.
|
|
679
|
+
|
|
703
680
|
## 16. Migrations and user management CLI
|
|
704
681
|
|
|
705
682
|
```bash
|
|
@@ -2,36 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
Deep reference for the React side of fastapi-rtk: Provider setup, ApiProvider, hooks (`useApi`, `useAuth`, `useForms`, …), `NextGenDataGrid`, dialogs, JsonForms, bulk actions, i18n. Load this when the user is working on React code inside an `src/` folder or anything that imports from `fastapi-rtk` (the JS package) or `@mantine/*`.
|
|
4
4
|
|
|
5
|
-
## Table of contents
|
|
6
|
-
|
|
7
|
-
1. Install and provider setup
|
|
8
|
-
2. The `ApiProvider` context
|
|
9
|
-
3. `useApi`: the CRUD hook
|
|
10
|
-
4. `useCustomFetch`: non-CRUD endpoints
|
|
11
|
-
5. `useInfo`: metadata-driven UIs
|
|
12
|
-
6. Auth: `useAuth` and OAuth
|
|
13
|
-
7. `NextGenDataGrid`: grids
|
|
14
|
-
8. Forms: `useForms` and dialogs
|
|
15
|
-
9. JsonForms for complex schemas
|
|
16
|
-
10. Bulk actions
|
|
17
|
-
11. Infinite scroll and lazy loading
|
|
18
|
-
12. Language / i18n (`useLang`, `LanguageSelector`)
|
|
19
|
-
13. Standard routes: `UserMenu`, `/users`, `/roles`, `/permissions`
|
|
20
|
-
14. Platform adapters (React Native / Expo)
|
|
21
|
-
15. Other notable exports
|
|
22
|
-
16. Rules of thumb
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
5
|
## 1. Install and provider setup
|
|
27
6
|
|
|
28
|
-
The toolkit expects Mantine as the UI kit.
|
|
7
|
+
The toolkit expects Mantine as the UI kit. Use `Stack`, `Group`, `Grid`, `SimpleGrid` for layout and Mantine inputs for one-off forms; mixing in a second UI kit drifts the theme.
|
|
29
8
|
|
|
30
9
|
```bash
|
|
31
|
-
pnpm add fastapi-rtk @mantine/core @mantine/dates @mantine/form @mantine/hooks
|
|
10
|
+
pnpm add fastapi-rtk @mantine/core @mantine/dates @mantine/form @mantine/hooks dayjs react-router
|
|
32
11
|
```
|
|
33
12
|
|
|
34
|
-
Peer deps: the four `@mantine/*` packages (`^9.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`). Everything else (mantine-react-table, TanStack Query, zustand, i18next, JsonForms) is bundled inside `fastapi-rtk`. Mantine >= 9.4.1 requires fastapi-rtk >= 2.8.0; older releases loop forever in the grid's `useColumns` ("Maximum update depth exceeded").
|
|
35
14
|
|
|
36
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).
|
|
37
16
|
|
|
@@ -79,7 +58,7 @@ Other `Provider` props: `langProps` (i18n extension, §12), `adapters` (§14), `
|
|
|
79
58
|
|
|
80
59
|
Key props:
|
|
81
60
|
|
|
82
|
-
- `resource_name`: **must match** the backend's `ModelRestApi.resource_name`. This is the contract; no magic fixes a mismatch.
|
|
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.
|
|
83
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`.
|
|
84
63
|
- `streaming`: switch the provider to infinite-scroll mode (§11).
|
|
85
64
|
- Behavior knobs: `relation`, `fetchInfo`, `clearInfoOnRefetch`, `clearDataOnRefetch`, `refetchInfoBasedOnLang`, `refetchDataBasedOnLang`, `resetQueryParamsOnPathChange`, plus raw TanStack overrides `infoQueryProps` / `dataQueryProps` / `streamingQueryProps`. `withApiProvider` is the HOC variant.
|
|
@@ -165,7 +144,7 @@ await addEntry({ name: "x" }); // POST /{resource}
|
|
|
165
144
|
await updateEntry(id, { name: "y" }); // PUT /{resource}/{id}
|
|
166
145
|
```
|
|
167
146
|
|
|
168
|
-
**Option B, use the TanStack Query namespace `rq`.** `fastapi-rtk` re-exports `@tanstack/react-query` as `rq` (`export * as rq from '@tanstack/react-query'` in `core/src/index.ts:
|
|
147
|
+
**Option B, use the TanStack Query namespace `rq`.** `fastapi-rtk` re-exports `@tanstack/react-query` as `rq` (`export * as rq from '@tanstack/react-query'` in `api/src/index.ts:38`, surfaced by `core/src/index.ts:94`; the sibling `mrt` namespace re-exports `mantine-react-table` from `core/src/index.ts:95`, with long-form aliases `reactQuery` / `mantineReactTable`). 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.
|
|
169
148
|
|
|
170
149
|
```jsx
|
|
171
150
|
import { useApi, rq } from "fastapi-rtk";
|
|
@@ -234,6 +213,8 @@ user?.permissions; // string[] of API NAMES the user holds any grant on
|
|
|
234
213
|
|
|
235
214
|
Gate routes/components on `user` and `user.roles`. The Provider keeps the session via cookie, so `user` is populated on every page load automatically.
|
|
236
215
|
|
|
216
|
+
An OAuth provider configured with `"backend": "jwt"` is for clients without a browser cookie store (React Native on `createReactNativeAdapters`, whose bearer transport stores the token); web apps default to `cookie` providers, since a bearer transport on web keeps the token in `localStorage`, readable by any script on the origin. A web app can still opt into JWT: `adapters={{ authTransport: createBearerTransport(webAdapters.storage) }}`, the provider's `redirect_url` at an app route, and `oauthCallback("google", window.location.search)` on that route. On React Native: point the provider's `redirect_url` at the app's deep link (`myapp://oauth/google`), pass the same URL as `createReactNativeAdapters({ ..., oauthRedirectUrl })` (a string for every provider, or `{ google: "myapp://oauth/google", github: "..." }` keyed by provider name), and call `oauthSignin("google", false)` (no popup). A provider missing from that map fails before any browser opens (`error` is `Could not sign in`, `originalError` names the provider). iOS ends the session on any URL with the redirect URL's scheme (so `myapp://oauth/google` and `myapp://oauth/github` are not told apart there), Android on the first deep link starting with it; for an `https` redirect on iOS 17.4+ pass `oauthPreferUniversalLinks: true` (needs the Associated Domains entitlement) to match host and path instead. The RN opener runs `webBrowser.openAuthSessionAsync(loginUrl, oauthRedirectUrl, { preferUniversalLinks })` and hands the redirected URL back, and `oauthSignin` forwards its `code` / `state` to `auth/oauth-authorized/<provider>`, stores the returned token through `authTransport.persistLogin`, and refetches the user; a failure sets `error` to `Could not sign in`. An app that receives the redirect another way calls `oauthCallback("google", redirectQuery)` from `useAuth()` with that URL's query string.
|
|
217
|
+
|
|
237
218
|
**`user.permissions` holds Api names, not permission names.** `auth/user` builds it as `select(Api.name)` joined through the user's roles (backend reference §14, "What the browser gets"), so `user.permissions.includes("ItemApi")` answers "has any grant on that Api" and nothing finer: `can_get` and `can_post` are indistinguishable there. It is the right gate for "may this user reach this resource at all", and it beats shipping a role list through the frontend config, since it follows the grants themselves and leaves no second list to keep in step. For per-verb detail read `useApi().info.permissions` (§5), which is that Api's `/_info` intersected with the user's grants.
|
|
238
219
|
|
|
239
220
|
## 7. `NextGenDataGrid`: grids
|
|
@@ -274,11 +255,11 @@ import { NextGenDataGrid } from "fastapi-rtk";
|
|
|
274
255
|
Key props:
|
|
275
256
|
|
|
276
257
|
- 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).
|
|
277
|
-
- 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/
|
|
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.
|
|
278
259
|
- `quickFilters`: one-click filter presets rendered above the grid (`quickFiltersComponent` swaps the renderer).
|
|
279
260
|
- `enableAdvancedFilters`: shows the structured filter builder.
|
|
280
261
|
- `queryOnlyVisibleColumns`: perf, only requests visible columns from the backend.
|
|
281
|
-
- `header` / `body` / `form`: per-column customization objects. `header` (keyed by column name): `filterComponent`, `filterProps`, `enableFilterModes` (per-operator component maps), `filterState` (external control), `filterSync`, `withLocalStorage` + `localStorageKey` (persist filter state), `defaultOpr`. `body` (keyed by column name): `truncate`, `component`, `separator` (multi-row cells), `highlight` / `highlightProps`, `tooltipProps`. `form` is keyed by `add` / `edit` / `view` / `delete`, each a JsonForms customizer applied to that auto-generated dialog plus dialog-level keys `initialView` / `dialogProps` (custom inputs:
|
|
262
|
+
- `header` / `body` / `form`: per-column customization objects. `header` (keyed by column name): `filterComponent`, `filterProps`, `enableFilterModes` (per-operator component maps), `filterState` (external control), `filterSync`, `withLocalStorage` + `localStorageKey` (persist filter state), `defaultOpr`. `body` (keyed by column name): `truncate`, `component`, `separator` (multi-row cells), `highlight` / `highlightProps`, `tooltipProps`. `form` is keyed by `add` / `edit` / `view` / `delete`, each a JsonForms customizer applied to that auto-generated dialog plus dialog-level keys `initialView` / `dialogProps` (custom inputs: jsonforms.md §3).
|
|
282
263
|
- `toolbar`: `customizer` hides (`null`) or replaces the built-in buttons (`toggleGlobalFilter`, `filter`, `refresh`, `add`, `download`, `showHideColumns`, `toggleDensePadding`, `toggleFullScreen`); `leftToolbar` / `centerToolbar` / `custom` add slots; the alert banner is customizable the same way.
|
|
283
264
|
- Misc: `table` (raw MRT options, merged as overrides on top of the generated ones), `optimizations`, `memo` (`'filters' | 'cells'`), `withBorder`, `wrapperProps`, `fetchOnMount`, `hideToolbar` / `hideActions` / `hidePagination` / `hideError`, `textFilterSeparator`, and a `children` render prop.
|
|
284
265
|
|
|
@@ -339,165 +320,9 @@ Mounting these dialogs **outside** a `NextGenDataGrid` (e.g. wiring CRUD onto a
|
|
|
339
320
|
|
|
340
321
|
## 9. JsonForms for complex schemas
|
|
341
322
|
|
|
342
|
-
When a form needs nested objects, arrays of subforms, or conditional sections, the built-in dialog form isn't enough; use `JsonFormsWithCustomizer`, which wraps `@jsonforms/react` with Mantine renderers.
|
|
343
|
-
|
|
344
|
-
```jsx
|
|
345
|
-
import { JsonFormsWithCustomizer } from "fastapi-rtk";
|
|
346
|
-
|
|
347
|
-
<JsonFormsWithCustomizer
|
|
348
|
-
schema={jsonSchema}
|
|
349
|
-
uischema={uiSchema}
|
|
350
|
-
data={formData}
|
|
351
|
-
onChange={({ data, errors }) => setState({ data, errors })}
|
|
352
|
-
/>;
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
For most CRUD forms you don't need this, since the default dialog plus `/_info` schema covers it. Reach for JsonForms when you're rendering user-defined schemas or deeply nested data. Full prop set: `schema`, `uischema`, `data`, `onChange`, `customizer`, `debounce`, `translations`, `hideWarnings`, `createTranslator`, `middleware`.
|
|
356
|
-
|
|
357
|
-
**Schema-level `allOf` / `if`/`then`/`else` resolve live against the form data.** `JsonFormsWithCustomizer` installs a JsonForms middleware that resolves schema conditionals on every data change and renders the branch-applied schema: the matching branch's `properties` merge recursively over the base (narrowing one nested field keeps its siblings), scalar keywords win per key (a narrowed `enum` replaces the base list), `required` unions at each level, and the combinator is stripped. Conditionals resolve at the root and inside nested object properties: a nested `if` evaluates against that object's own data slice, while a root-level `if` sees the whole form data and may reference nested paths (and its `then`/`else` may narrow nested fields). Not resolved: `anyOf` / `oneOf` and conditionals inside array `items`. A supplied `uischema` is preserved; with none, the UI schema is regenerated from the resolved schema. Exported for programmatic use: `resolveConditionalSchema(schema, data, ajv)`, `createConditionalMiddleware(schema, { regenerateUischema })`, and `composeMiddleware(outer, inner)`; a caller-supplied `middleware` prop is composed to run underneath the conditional middleware.
|
|
358
|
-
|
|
359
|
-
### 9.1 Per-control props via `options.props` and `options.ifProps`
|
|
360
|
-
|
|
361
|
-
Every Mantine-themed control built on `useCommonProps` (the hook backing the toolkit's JsonForms renderers) spreads two extra props blocks from the uischema, in this order:
|
|
362
|
-
|
|
363
|
-
1. `uischema.options.props`: static props passed straight to the underlying component.
|
|
364
|
-
2. `uischema.options.ifProps`: dynamic props chosen by an `if/then/else` schema evaluated against the **whole form data** (not the control's own value).
|
|
365
|
-
|
|
366
|
-
`ifProps` only works on control components. Both `then` and `else` are optional; the matching branch's `props` object is spread onto the control, the other is dropped.
|
|
367
|
-
|
|
368
|
-
```js
|
|
369
|
-
{
|
|
370
|
-
"type": "Control",
|
|
371
|
-
"scope": "#/properties/details",
|
|
372
|
-
"options": {
|
|
373
|
-
"ifProps": {
|
|
374
|
-
"if": {
|
|
375
|
-
"properties": { "category": { "const": "C" } },
|
|
376
|
-
"required": ["category"]
|
|
377
|
-
},
|
|
378
|
-
"then": { "props": { "required": true } },
|
|
379
|
-
"else": { "props": { "disabled": true } }
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
**Conditional `required` indicator.** A schema-level `if` / `then.required` produces the conditional asterisk and validation on its own: the resolver merges `then.required` into the live `required` array (at the root or inside a nested object) and JsonForms derives the asterisk from it, updating as the trigger field changes. No `options.ifProps` layer is needed for `required`; keep `ifProps` for non-schema props (`disabled`, `placeholder`, a custom `component`). Rule effects cover only `SHOW` / `HIDE` / `ENABLE` / `DISABLE` (there is no `REQUIRE`), and a static `options.props.required: true` is read unconditionally.
|
|
386
|
-
|
|
387
|
-
```js
|
|
388
|
-
// schema (the uischema needs nothing on the dependent field)
|
|
389
|
-
{
|
|
390
|
-
"type": "object",
|
|
391
|
-
"required": ["category"],
|
|
392
|
-
"properties": {
|
|
393
|
-
"category": { "type": "string", "enum": ["A", "B", "C"] },
|
|
394
|
-
"details": { "type": "string" }
|
|
395
|
-
},
|
|
396
|
-
"if": { "properties": { "category": { "const": "C" } }, "required": ["category"] },
|
|
397
|
-
"then": { "required": ["details"] }
|
|
398
|
-
}
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
When a field is **unconditionally** required, lift it onto top-level `required: [...]`; JsonForms derives the asterisk from the schema, no `options.props.required` needed.
|
|
402
|
-
|
|
403
|
-
**Pitfall: always pair `properties` with `required` inside `if`.** A JSON Schema `if` with only `{ "properties": { "x": { "const": "Y" } } }` matches when `x` is absent (an undefined value satisfies the property schema trivially), so `then.props.required: true` fires on first render before the user has touched the selector. Adding `"required": ["x"]` gates the match on "x is set AND equals Y".
|
|
404
|
-
|
|
405
|
-
**`ifProps` conditions always evaluate against the form root, not the control's container.** `useIfProps` reads `core.data` from `useJsonForms()`, which is the whole form data, not the sub-slice the control sits in. A conditional control deep inside `#/properties/details/properties/subcategory` must reference siblings by their root path (`properties.details.properties.category`, not `properties.category`). Schema-level conditionals differ by placement: a root-level `allOf[].if` is also root-scoped, while a conditional inside a nested object property evaluates against that object's own data slice.
|
|
406
|
-
|
|
407
|
-
Since js 2.0.1, `useIfProps` short-circuits on `null` root data (no values to inspect, no useful `then`), so an `if` never fires against effectively-empty data; matching a _property_ whose value is `null` still works (`if: { properties: { x: { const: null } }, required: ["x"] }`) because the root is still an object.
|
|
408
|
-
|
|
409
|
-
### 9.2 Custom Input components via the `customizer` slot
|
|
323
|
+
When a form needs nested objects, arrays of subforms, or conditional sections, the built-in dialog form isn't enough; use `JsonFormsWithCustomizer`, which wraps `@jsonforms/react` with Mantine renderers. For most CRUD forms you don't need it, since the default dialog plus the `/_info` schema covers them (§8).
|
|
410
324
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
**The slot, and the two props every slot factory receives:**
|
|
414
|
-
|
|
415
|
-
```jsx
|
|
416
|
-
const customizer = {
|
|
417
|
-
[key]: {
|
|
418
|
-
component, // ReactNode | ((args) => ReactNode) : REPLACE the input
|
|
419
|
-
props, // object | ((args) => object) : MERGE props onto the built-in
|
|
420
|
-
},
|
|
421
|
-
__: { debounce: true }, // reserved: { debounce, debounceOptions: { delay, flushOnUnmount }, setNull }, not a slot
|
|
422
|
-
};
|
|
423
|
-
```
|
|
424
|
-
|
|
425
|
-
`args` is always `{ componentProps, jsonFormsProps }`:
|
|
426
|
-
|
|
427
|
-
- `componentProps` : the fully computed Mantine props the built-in would have used: `value`, `onChange` (already wired to JsonForms `handleChange(path, value)`), `onBlur`, `label`, `description`, `required`, `disabled`, `error`, plus `uischema.options.props` and `ifProps` (§9.1). Spread it onto a value-based Mantine input and the field is controlled and validated for free.
|
|
428
|
-
- `jsonFormsProps` : the raw control props (`data`, `handleChange`, `path`, `schema`, `uischema`, `enabled`, `errors`, `visible`, `id`). Reach here when `componentProps` is not enough, or call `useCommonProps(jsonFormsProps)` (from `fastapi-rtk`) to have the library rebuild the `handleChange(path, value)` wrapper for you.
|
|
429
|
-
|
|
430
|
-
`key` is a **renderer name** (`Text`, `Number`, `Integer`, `Boolean`, `Date`, `DateTime`, `Time`, `Enum`, `EnumArray`, `OneOfEnum`, `File`, `Image`, `Json`, `Object`, `Label`, `Table`, `Array`, `Array.Accordion`), a **JsonForms path / field name** (`status`, `details.category`), or `all`. Precedence when several match: `all` < renderer-name < path (slots deep-merge in that order, most specific wins). Since 2.8.0 every structural wrapper is addressable too: `Table.Thead` / `Table.Tbody` / `Table.Empty`, `Table.Header.*` (`Tr`, `Actions`, `Validation`, `Toolbar`, `Wrapper`, `Flex`), `Table.Row.*` (`Tr`, `Td`, `Actions`, `Grid`, `Up`, `Down`, `Remove`), per-row / per-cell addressing (`Table.Row.{index}`, `Table.Row.{index}.{subPath}`, `Table.Row.{subPath}`), `Array.Stack` / `Array.Items` / `Array.Empty` / `Array.Header.*` / `Array.Item.*` / `Array.DeleteDialog`, `File.Box` / `Image.Box`, and `Label.{text}`. (Collision escape: when a real field is named `all`, the wildcard moves to the exported `specialKey.all`.)
|
|
431
|
-
|
|
432
|
-
**Write the component** as a plain Mantine component. For a blob field, `JSON.parse` on read and `JSON.stringify` on write; keep spreading `{...componentProps}` so the label / description / error wrapper still renders:
|
|
433
|
-
|
|
434
|
-
```jsx
|
|
435
|
-
import { TagsInput } from "@mantine/core";
|
|
436
|
-
|
|
437
|
-
function TagsField({ componentProps }) {
|
|
438
|
-
const value = componentProps.value ? JSON.parse(componentProps.value) : [];
|
|
439
|
-
return (
|
|
440
|
-
<TagsInput
|
|
441
|
-
{...componentProps}
|
|
442
|
-
value={value}
|
|
443
|
-
onChange={(next) => componentProps.onChange(JSON.stringify(next))}
|
|
444
|
-
/>
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
**Register it by key** (no tester, no `{ tester, renderer }`, no `renderers` array). Three equivalent entry points:
|
|
450
|
-
|
|
451
|
-
1. **On a grid's add/edit dialogs** (the common CRUD path) via `NextGenDataGrid`'s `form` prop. `form.add` / `form.edit` are customizer trees keyed by field name; the grid forwards them to its internal `<AddDialog jsonForms>` / `<EditDialog jsonForms>`. Dialog-level keys (`initialView`, `dialogProps`) sit next to the field keys.
|
|
452
|
-
|
|
453
|
-
```jsx
|
|
454
|
-
<NextGenDataGrid
|
|
455
|
-
form={{
|
|
456
|
-
add: {
|
|
457
|
-
tags: {
|
|
458
|
-
component: ({ componentProps }) => (
|
|
459
|
-
<TagsField componentProps={componentProps} />
|
|
460
|
-
),
|
|
461
|
-
},
|
|
462
|
-
},
|
|
463
|
-
edit: {
|
|
464
|
-
tags: {
|
|
465
|
-
component: ({ componentProps }) => (
|
|
466
|
-
<TagsField componentProps={componentProps} />
|
|
467
|
-
),
|
|
468
|
-
},
|
|
469
|
-
},
|
|
470
|
-
}}
|
|
471
|
-
/>
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
2. **On the standalone dialogs**: `<AddDialog jsonForms={customizer} />` / `<EditDialog jsonForms={customizer} />`.
|
|
475
|
-
|
|
476
|
-
3. **On a raw element** for a bespoke (non-CRUD) form: `<JsonFormsWithCustomizer schema={schema} uischema={uischema} data={data} onChange={onChange} customizer={customizer} />`.
|
|
477
|
-
|
|
478
|
-
**`component` swaps the whole input; `props` only tweaks the built-in.** Use `props` to nudge every control of a type, or to derive a prop from the field's own state:
|
|
479
|
-
|
|
480
|
-
```jsx
|
|
481
|
-
const customizer = {
|
|
482
|
-
Enum: { props: { searchable: true } }, // every Enum control
|
|
483
|
-
all: {
|
|
484
|
-
props: ({ componentProps }) => ({ withAsterisk: componentProps.required }),
|
|
485
|
-
},
|
|
486
|
-
};
|
|
487
|
-
```
|
|
488
|
-
|
|
489
|
-
A `props` value of `null` explicitly unsets that prop on the built-in; `undefined` is a no-op that leaves the prior value. Function-valued slots receive the fully-merged default props as `componentProps` (since 2.8.0), including a structural slot's computed attributes and children, so an override can wrap or reuse exactly what the built-in would have rendered.
|
|
490
|
-
|
|
491
|
-
**Pitfall - a hook-using custom input must be returned as an element, not passed as a component type.** The slot value runs through `parseFromValuesOrFunc`, which _calls_ a function slot inline (`slot({ componentProps, jsonFormsProps })`). Passing the component type directly (`component: MyInput`) therefore executes `MyInput`'s hooks inside the built-in renderer's render pass, a rule-of-hooks violation. Always wrap it in an arrow that returns an element so React mounts it normally:
|
|
492
|
-
|
|
493
|
-
```jsx
|
|
494
|
-
const customizer = {
|
|
495
|
-
color: { component: (args) => <ColorField {...args} /> }, // arrow returns an element -> hooks safe
|
|
496
|
-
// NOT: { color: { component: ColorField } } // called inline -> hooks run in the wrong pass
|
|
497
|
-
};
|
|
498
|
-
```
|
|
499
|
-
|
|
500
|
-
**Escape hatch - a genuinely new control type** not covered by any built-in renderer: pass a standard `@jsonforms` `renderers` array (your `withJsonFormsControlProps` component + a `rankWith` tester) through the dialog's `jsonFormsProps`, which spreads onto the inner `<JsonForms>` (`<AddDialog jsonFormsProps={{ renderers: [...myRenderers] }} />`). Include the built-in `mantineRenderers` in that array or you lose every default input. TypeScript nit: `jsonFormsProps` is typed `Partial<JsonFormsWithCustomizerProps>`, which omits `renderers`; the runtime spread honors it anyway, so cast to pass it. The renderer-building kit is exported for this path: `mantineRenderers`, `mantineCells`, and the per-type prop hooks (`useContextProps`, `useTextProps`, `useDateProps`, `useFileProps`, `useNumberProps`, ...). Prefer the `customizer` for anything that maps onto an existing input: it keeps the Mantine theme, i18n, debounce, and error wiring.
|
|
325
|
+
**Read `jsonforms.md` for the whole subsystem**: schema-level `allOf` / `if`/`then`/`else` resolution, per-control props via `options.props` and `options.ifProps`, and custom inputs through the `customizer` slot.
|
|
501
326
|
|
|
502
327
|
## 10. Bulk actions
|
|
503
328
|
|
|
@@ -648,15 +473,5 @@ Cookie login is on by default on the backend; the JWT endpoint exists only when
|
|
|
648
473
|
- Headless fetcher layer for code outside any provider: `buildRequest`, `getItems` / `getItem` / `createItem` / `updateItem` / `deleteItem` / `getInfo`, `formCreateItem` / `formUpdateItem`, `uploadItems` / `downloadItems`, `postBulk`, plus `useAdapters` / `webAdapters`.
|
|
649
474
|
- Legacy `DataGrid` is still exported alongside `NextGenDataGrid`; standalone `ActionIcons` (`Actions`, `Add`, `Delete`, `Download`, `Edit`, `Refresh`, `Upload`, `View`) and `Filter` compose custom layouts.
|
|
650
475
|
- Utilities: `deepMerge`, `parseFromValuesOrFunc`, `createSafeContext`, `getItemId`, `urlJoin`, `Memoizer`, `FastAPIRTKLogger`; hooks `useActionIcon`, `useSyncValues`, `useTrueOnce`.
|
|
651
|
-
- Router integration targets `react-router` v7.
|
|
476
|
+
- Router integration targets `react-router` v7 or v8 (peer range `^7.5.3 || ^8.0.0`).
|
|
652
477
|
- Streaming loads render a progress bar instead of a blocking overlay (2.8.0+); toolbar buttons auto-hide when the backend disables the matching feature.
|
|
653
|
-
|
|
654
|
-
## 16. Rules of thumb
|
|
655
|
-
|
|
656
|
-
1. **Never `fetch` a standard resource.** Use `useApi`. Direct fetch calls miss session refresh, error handling, and `/_info`-driven UI hydration.
|
|
657
|
-
2. **Respect the provider hierarchy.** If `useApi` throws "not inside an ApiProvider", wrap the component; don't switch to `useCustomFetch` as a workaround.
|
|
658
|
-
3. **`resource_name` is the contract.** If the backend says `items`, the frontend says `items`. Not `item`, not `/items`, not `Items`.
|
|
659
|
-
4. **Configure the grid; don't wrap it.** The `header` / `body` / `form` props cover almost all customization. Wrapping breaks selection/filter sync.
|
|
660
|
-
5. **Let `/_info` drive UI.** When the backend adds a column or changes permissions, a well-built frontend picks it up for free. Hardcoding column lists is a per-release maintenance tax.
|
|
661
|
-
6. **Mantine layout primitives only.** Use `Stack`, `Group`, `Grid`, `SimpleGrid` for layout, Mantine inputs for one-off forms. Mixing Mantine with another UI kit leads to theme drift.
|
|
662
|
-
7. **CSS import order: Mantine → fastapi-rtk.** Otherwise grid/form styles render oddly because fastapi-rtk CSS depends on Mantine tokens.
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# JsonForms reference (fastapi-rtk React)
|
|
2
|
+
|
|
3
|
+
Deep reference for `JsonFormsWithCustomizer`: schema-level conditionals, per-control props, and custom inputs through the `customizer` slot. Load this only when a form needs nested objects, arrays of subforms, conditional sections, or a custom input; the default dialog form plus the `/_info` schema covers ordinary CRUD, which is frontend.md §8.
|
|
4
|
+
|
|
5
|
+
## 1. When to reach for it
|
|
6
|
+
|
|
7
|
+
When a form needs nested objects, arrays of subforms, or conditional sections, the built-in dialog form isn't enough; use `JsonFormsWithCustomizer`, which wraps `@jsonforms/react` with Mantine renderers. Input cells cover DateTime, Date, Time, File, Image, Boolean, Enum, Text, Number and full layout/control renderers.
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import { JsonFormsWithCustomizer } from "fastapi-rtk";
|
|
11
|
+
|
|
12
|
+
<JsonFormsWithCustomizer
|
|
13
|
+
schema={jsonSchema}
|
|
14
|
+
uischema={uiSchema}
|
|
15
|
+
data={formData}
|
|
16
|
+
onChange={({ data, errors }) => setState({ data, errors })}
|
|
17
|
+
/>;
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For most CRUD forms you don't need this, since the default dialog plus `/_info` schema covers it. Reach for JsonForms when you're rendering user-defined schemas or deeply nested data. Full prop set: `schema`, `uischema`, `data`, `onChange`, `customizer`, `debounce`, `translations`, `hideWarnings`, `createTranslator`, `middleware`.
|
|
21
|
+
|
|
22
|
+
**Schema-level `allOf` / `if`/`then`/`else` resolve live against the form data.** `JsonFormsWithCustomizer` installs a JsonForms middleware that resolves schema conditionals on every data change and renders the branch-applied schema: the matching branch's `properties` merge recursively over the base (narrowing one nested field keeps its siblings), scalar keywords win per key (a narrowed `enum` replaces the base list), `required` unions at each level, and the combinator is stripped. Conditionals resolve at the root and inside nested object properties: a nested `if` evaluates against that object's own data slice, while a root-level `if` sees the whole form data and may reference nested paths (and its `then`/`else` may narrow nested fields). Not resolved: `anyOf` / `oneOf` and conditionals inside array `items`. A supplied `uischema` is preserved; with none, the UI schema is regenerated from the resolved schema. Exported for programmatic use: `resolveConditionalSchema(schema, data, ajv)`, `createConditionalMiddleware(schema, { regenerateUischema })`, and `composeMiddleware(outer, inner)`; a caller-supplied `middleware` prop is composed to run underneath the conditional middleware.
|
|
23
|
+
|
|
24
|
+
## 2. Per-control props via `options.props` and `options.ifProps`
|
|
25
|
+
|
|
26
|
+
Every Mantine-themed control built on `useCommonProps` (the hook backing the toolkit's JsonForms renderers) spreads two extra props blocks from the uischema, in this order:
|
|
27
|
+
|
|
28
|
+
1. `uischema.options.props`: static props passed straight to the underlying component.
|
|
29
|
+
2. `uischema.options.ifProps`: dynamic props chosen by an `if/then/else` schema evaluated against the **whole form data** (not the control's own value).
|
|
30
|
+
|
|
31
|
+
`ifProps` only works on control components. Both `then` and `else` are optional; the matching branch's `props` object is spread onto the control, the other is dropped.
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
{
|
|
35
|
+
"type": "Control",
|
|
36
|
+
"scope": "#/properties/details",
|
|
37
|
+
"options": {
|
|
38
|
+
"ifProps": {
|
|
39
|
+
"if": {
|
|
40
|
+
"properties": { "category": { "const": "C" } },
|
|
41
|
+
"required": ["category"]
|
|
42
|
+
},
|
|
43
|
+
"then": { "props": { "required": true } },
|
|
44
|
+
"else": { "props": { "disabled": true } }
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Conditional `required` indicator.** A schema-level `if` / `then.required` produces the conditional asterisk and validation on its own: the resolver merges `then.required` into the live `required` array (at the root or inside a nested object) and JsonForms derives the asterisk from it, updating as the trigger field changes. No `options.ifProps` layer is needed for `required`; keep `ifProps` for non-schema props (`disabled`, `placeholder`, a custom `component`). Rule effects cover only `SHOW` / `HIDE` / `ENABLE` / `DISABLE` (there is no `REQUIRE`), and a static `options.props.required: true` is read unconditionally.
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
// schema (the uischema needs nothing on the dependent field)
|
|
54
|
+
{
|
|
55
|
+
"type": "object",
|
|
56
|
+
"required": ["category"],
|
|
57
|
+
"properties": {
|
|
58
|
+
"category": { "type": "string", "enum": ["A", "B", "C"] },
|
|
59
|
+
"details": { "type": "string" }
|
|
60
|
+
},
|
|
61
|
+
"if": { "properties": { "category": { "const": "C" } }, "required": ["category"] },
|
|
62
|
+
"then": { "required": ["details"] }
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
When a field is **unconditionally** required, lift it onto top-level `required: [...]`; JsonForms derives the asterisk from the schema, no `options.props.required` needed.
|
|
67
|
+
|
|
68
|
+
**Pitfall: always pair `properties` with `required` inside `if`.** A JSON Schema `if` with only `{ "properties": { "x": { "const": "Y" } } }` matches when `x` is absent (an undefined value satisfies the property schema trivially), so `then.props.required: true` fires on first render before the user has touched the selector. Adding `"required": ["x"]` gates the match on "x is set AND equals Y".
|
|
69
|
+
|
|
70
|
+
**`ifProps` conditions always evaluate against the form root, not the control's container.** `useIfProps` reads `core.data` from `useJsonForms()`, which is the whole form data, not the sub-slice the control sits in. A conditional control deep inside `#/properties/details/properties/subcategory` must reference siblings by their root path (`properties.details.properties.category`, not `properties.category`). Schema-level conditionals differ by placement: a root-level `allOf[].if` is also root-scoped, while a conditional inside a nested object property evaluates against that object's own data slice.
|
|
71
|
+
|
|
72
|
+
Since js 2.0.1, `useIfProps` short-circuits on `null` root data (no values to inspect, no useful `then`), so an `if` never fires against effectively-empty data; matching a _property_ whose value is `null` still works (`if: { properties: { x: { const: null } }, required: ["x"] }`) because the root is still an object.
|
|
73
|
+
|
|
74
|
+
## 3. Custom Input components via the `customizer` slot
|
|
75
|
+
|
|
76
|
+
To add a custom input, you do **not** register a `withJsonFormsControlProps` renderer + a `rankWith` tester into a renderers array. `JsonFormsWithCustomizer` hardcodes `renderers={mantineRenderers}` and ships all the classic renderers, cells, and testers inside `fastapi-rtk`; you consume that machinery, you don't re-declare it. A custom input is a plain Mantine component bound to a schema field **by key**, through a `{ component, props }` customizer slot.
|
|
77
|
+
|
|
78
|
+
**The slot, and the two props every slot factory receives:**
|
|
79
|
+
|
|
80
|
+
```jsx
|
|
81
|
+
const customizer = {
|
|
82
|
+
[key]: {
|
|
83
|
+
component, // ReactNode | ((args) => ReactNode) : REPLACE the input
|
|
84
|
+
props, // object | ((args) => object) : MERGE props onto the built-in
|
|
85
|
+
},
|
|
86
|
+
__: { debounce: true }, // reserved: { debounce, debounceOptions: { delay, flushOnUnmount }, setNull }, not a slot
|
|
87
|
+
};
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`args` is always `{ componentProps, jsonFormsProps }`:
|
|
91
|
+
|
|
92
|
+
- `componentProps` : the fully computed Mantine props the built-in would have used: `value`, `onChange` (already wired to JsonForms `handleChange(path, value)`), `onBlur`, `label`, `description`, `required`, `disabled`, `error`, plus `uischema.options.props` and `ifProps` (§2). Spread it onto a value-based Mantine input and the field is controlled and validated for free.
|
|
93
|
+
- `jsonFormsProps` : the raw control props (`data`, `handleChange`, `path`, `schema`, `uischema`, `enabled`, `errors`, `visible`, `id`). Reach here when `componentProps` is not enough, or call `useCommonProps(jsonFormsProps)` (from `fastapi-rtk`) to have the library rebuild the `handleChange(path, value)` wrapper for you.
|
|
94
|
+
|
|
95
|
+
`key` is a **renderer name** (`Text`, `Number`, `Integer`, `Boolean`, `Date`, `DateTime`, `Time`, `Enum`, `EnumArray`, `OneOfEnum`, `File`, `Image`, `Json`, `Object`, `Label`, `Table`, `Array`, `Array.Accordion`), a **JsonForms path / field name** (`status`, `details.category`), or `all`. Precedence when several match: `all` < renderer-name < path (slots deep-merge in that order, most specific wins). Since 2.8.0 every structural wrapper is addressable too: `Table.Thead` / `Table.Tbody` / `Table.Empty`, `Table.Header.*` (`Tr`, `Actions`, `Validation`, `Toolbar`, `Wrapper`, `Flex`), `Table.Row.*` (`Tr`, `Td`, `Actions`, `Grid`, `Up`, `Down`, `Remove`), per-row / per-cell addressing (`Table.Row.{index}`, `Table.Row.{index}.{subPath}`, `Table.Row.{subPath}`), `Array.Stack` / `Array.Items` / `Array.Empty` / `Array.Header.*` / `Array.Item.*` / `Array.DeleteDialog`, `File.Box` / `Image.Box`, and `Label.{text}`. (Collision escape: when a real field is named `all`, the wildcard moves to the exported `specialKey.all`.)
|
|
96
|
+
|
|
97
|
+
**Write the component** as a plain Mantine component. For a blob field, `JSON.parse` on read and `JSON.stringify` on write; keep spreading `{...componentProps}` so the label / description / error wrapper still renders:
|
|
98
|
+
|
|
99
|
+
```jsx
|
|
100
|
+
import { TagsInput } from "@mantine/core";
|
|
101
|
+
|
|
102
|
+
function TagsField({ componentProps }) {
|
|
103
|
+
const value = componentProps.value ? JSON.parse(componentProps.value) : [];
|
|
104
|
+
return (
|
|
105
|
+
<TagsInput
|
|
106
|
+
{...componentProps}
|
|
107
|
+
value={value}
|
|
108
|
+
onChange={(next) => componentProps.onChange(JSON.stringify(next))}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Register it by key** (no tester, no `{ tester, renderer }`, no `renderers` array). Three equivalent entry points:
|
|
115
|
+
|
|
116
|
+
1. **On a grid's add/edit dialogs** (the common CRUD path) via `NextGenDataGrid`'s `form` prop. `form.add` / `form.edit` are customizer trees keyed by field name; the grid forwards them to its internal `<AddDialog jsonForms>` / `<EditDialog jsonForms>`. Dialog-level keys (`initialView`, `dialogProps`) sit next to the field keys.
|
|
117
|
+
|
|
118
|
+
```jsx
|
|
119
|
+
<NextGenDataGrid
|
|
120
|
+
form={{
|
|
121
|
+
add: {
|
|
122
|
+
tags: {
|
|
123
|
+
component: ({ componentProps }) => (
|
|
124
|
+
<TagsField componentProps={componentProps} />
|
|
125
|
+
),
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
edit: {
|
|
129
|
+
tags: {
|
|
130
|
+
component: ({ componentProps }) => (
|
|
131
|
+
<TagsField componentProps={componentProps} />
|
|
132
|
+
),
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
}}
|
|
136
|
+
/>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
2. **On the standalone dialogs**: `<AddDialog jsonForms={customizer} />` / `<EditDialog jsonForms={customizer} />`.
|
|
140
|
+
|
|
141
|
+
3. **On a raw element** for a bespoke (non-CRUD) form: `<JsonFormsWithCustomizer schema={schema} uischema={uischema} data={data} onChange={onChange} customizer={customizer} />`.
|
|
142
|
+
|
|
143
|
+
**`component` swaps the whole input; `props` only tweaks the built-in.** Use `props` to nudge every control of a type, or to derive a prop from the field's own state:
|
|
144
|
+
|
|
145
|
+
```jsx
|
|
146
|
+
const customizer = {
|
|
147
|
+
Enum: { props: { searchable: true } }, // every Enum control
|
|
148
|
+
all: {
|
|
149
|
+
props: ({ componentProps }) => ({ withAsterisk: componentProps.required }),
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
A `props` value of `null` explicitly unsets that prop on the built-in; `undefined` is a no-op that leaves the prior value. Function-valued slots receive the fully-merged default props as `componentProps` (since 2.8.0), including a structural slot's computed attributes and children, so an override can wrap or reuse exactly what the built-in would have rendered.
|
|
155
|
+
|
|
156
|
+
**Pitfall - a hook-using custom input must be returned as an element, not passed as a component type.** The slot value runs through `parseFromValuesOrFunc`, which _calls_ a function slot inline (`slot({ componentProps, jsonFormsProps })`). Passing the component type directly (`component: MyInput`) therefore executes `MyInput`'s hooks inside the built-in renderer's render pass, a rule-of-hooks violation. Always wrap it in an arrow that returns an element so React mounts it normally:
|
|
157
|
+
|
|
158
|
+
```jsx
|
|
159
|
+
const customizer = {
|
|
160
|
+
color: { component: (args) => <ColorField {...args} /> }, // arrow returns an element -> hooks safe
|
|
161
|
+
// NOT: { color: { component: ColorField } } // called inline -> hooks run in the wrong pass
|
|
162
|
+
};
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Escape hatch - a genuinely new control type** not covered by any built-in renderer: pass a standard `@jsonforms` `renderers` array (your `withJsonFormsControlProps` component + a `rankWith` tester) through the dialog's `jsonFormsProps`, which spreads onto the inner `<JsonForms>` (`<AddDialog jsonFormsProps={{ renderers: [...myRenderers] }} />`). Include the built-in `mantineRenderers` in that array or you lose every default input. TypeScript nit: `jsonFormsProps` is typed `Partial<JsonFormsWithCustomizerProps>`, which omits `renderers`; the runtime spread honors it anyway, so cast to pass it. The renderer-building kit is exported for this path: `mantineRenderers`, `mantineCells`, and the per-type prop hooks (`useContextProps`, `useTextProps`, `useDateProps`, `useFileProps`, `useNumberProps`, ...). Prefer the `customizer` for anything that maps onto an existing input: it keeps the Mantine theme, i18n, debounce, and error wiring.
|
|
@@ -18,11 +18,11 @@ Before 2.0.1, `useIfProps` early-returned only on `data === undefined`. If the r
|
|
|
18
18
|
}
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Fixed in 2.0.1 (`useIfProps` short-circuits on `null` root data); the explicit `type: "object"` gate is unnecessary on current versions (
|
|
21
|
+
Fixed in 2.0.1 (`useIfProps` short-circuits on `null` root data); the explicit `type: "object"` gate is unnecessary on current versions (jsonforms.md §2).
|
|
22
22
|
|
|
23
23
|
## Schema-level `allOf` / `if`/`then`/`else` hangs the form (js, fastapi-rtk <= 2.9.4)
|
|
24
24
|
|
|
25
|
-
Through 2.9.4, `JsonFormsWithCustomizer` passed schema combinators straight to JsonForms. With no `uischema`, `Generate.uiSchema` treats an object schema carrying a combinator as a leaf and emits a `Control` scoped to the object itself; the object control then regenerates the same UI schema for its detail view and recurses until the render loop hangs. Applies to a combinator at the root or on any nested object property. Workaround on those versions: pass an explicit `uischema` whose controls target the leaf properties (never a bare control scoped to the combinator-carrying object), or resolve the conditionals out of the schema before passing it in. Later releases resolve schema conditionals automatically via middleware (
|
|
25
|
+
Through 2.9.4, `JsonFormsWithCustomizer` passed schema combinators straight to JsonForms. With no `uischema`, `Generate.uiSchema` treats an object schema carrying a combinator as a leaf and emits a `Control` scoped to the object itself; the object control then regenerates the same UI schema for its detail view and recurses until the render loop hangs. Applies to a combinator at the root or on any nested object property. Workaround on those versions: pass an explicit `uischema` whose controls target the leaf properties (never a bare control scoped to the combinator-carrying object), or resolve the conditionals out of the schema before passing it in. Later releases resolve schema conditionals automatically via middleware (jsonforms.md §1).
|
|
26
26
|
|
|
27
27
|
## Conditional `required` asterisk needs a two-layer `ifProps` recipe (js, fastapi-rtk <= 2.9.4)
|
|
28
28
|
|
|
@@ -42,4 +42,4 @@ Without the schema resolver, a schema-level `if` / `then.required` only gates AJ
|
|
|
42
42
|
}
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Later releases derive the conditional asterisk from the schema alone, including inside nested objects (
|
|
45
|
+
Later releases derive the conditional asterisk from the schema alone, including inside nested objects (jsonforms.md §2).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# rtk-developer
|
|
2
|
+
|
|
3
|
+
The FastAPI React Toolkit is ONE framework with two halves designed to fit together. Treat it as a single system, not two independent stacks.
|
|
4
|
+
|
|
5
|
+
## The core concept
|
|
6
|
+
|
|
7
|
+
The value of fastapi-rtk is convention. Define a SQLAlchemy model, register a `ModelRestApi` with `resource_name="items"`, and the backend exposes full CRUD at `/api/v1/items`. Wrap a component in `<ApiProvider resource_name="items">`, call `useApi()`, and you get `data`, `loading`, `addEntry`, `updateEntry`, `deleteEntry` with no fetch code and no manual types.
|
|
8
|
+
|
|
9
|
+
**The `resource_name` string is the contract, and a typo breaks it silently.**
|
|
10
|
+
|
|
11
|
+
So most features have a backend half and a frontend half that must line up. Work out which side(s) a request touches before writing code:
|
|
12
|
+
|
|
13
|
+
| Feature | Backend piece | Frontend piece |
|
|
14
|
+
| ------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
15
|
+
| CRUD on a model | `ModelRestApi` | `ApiProvider` + `useApi` |
|
|
16
|
+
| List/detail views | `list_columns`, `show_columns` | `NextGenDataGrid` auto-columns + `columnProps` |
|
|
17
|
+
| Add/edit forms | `add_columns`, `edit_columns`, hooks | `useForms(kind)` → `setItem` + `setOpened`, `AddDialog`/`EditDialog` |
|
|
18
|
+
| Filtering | `base_filters`, `FilterXxx` | `initialQueryParams`, `quickFilters` (`{name, value:[{col,opr,value}]}`) |
|
|
19
|
+
| File / image upload | `FileColumn`, `ImageColumn`, file manager | form auto-detects and renders file input |
|
|
20
|
+
| Bulk actions | method `bulk_<handler>(body, query, session)` on the Api class | `useBulkActions` → `dispatchBulkActions(handler, ids)` |
|
|
21
|
+
| Permissions | `ROLES` config + `@protect` | `useAuth`, route guards |
|
|
22
|
+
| Custom endpoint | `BaseApi` + `@expose` | `rq` wrapper / `useCustomFetch` (last resort) |
|
|
23
|
+
| Platform adapters | auth endpoints (cookie `auth/login`; JWT needs `AUTH_LOGIN_JWT`) | `adapters` prop on `Provider` (web defaults; RN via `fastapi-rtk/react-native-adapters`) |
|
|
24
|
+
|
|
25
|
+
## Cross-cutting rules
|
|
26
|
+
|
|
27
|
+
1. **Never bypass the toolkit.** Backend: prefer `ModelRestApi` over hand-rolled routes and add behavior through `pre_*` / `post_*` hooks. `@expose` is for an endpoint whose logic is NOT CRUD on a model (an RPC-style call, an external-API proxy, a job with no model row); "do something extra when a row is added, edited or fetched" is a hook, not a new route. Frontend: never `fetch` or `axios` a standard resource, and render resource rows with `NextGenDataGrid` configured through its props, not a hand-built `<Table>` or a `.map`. Dropping to raw APIs suits genuinely custom behavior; doing it out of habit fights the framework. An ad-hoc route registered directly on the FastAPI app needs `tags=["<RegisteredApiClass>"]` to appear in `export api-docs` output.
|
|
28
|
+
2. **Align `resource_name` exactly** on both sides.
|
|
29
|
+
3. **Expose a different column projection per operation** rather than one model-wide set, and let the grid render from `/_info`. Do not overfetch on the model when the view already filters.
|
|
30
|
+
4. **Async-first.** Async drivers throughout, hooks are `async def`, and a sync portion mixed into async gets wrapped in `smart_run()`.
|
|
31
|
+
5. **Relationship filtering happens on the backend**, never by filtering dropdown options in JS. The form picks the backend's answer up automatically.
|
|
32
|
+
6. **Permissions are declarative**, configured rather than checked inline. Keep ad-hoc role checks out of business logic.
|
|
33
|
+
|
|
34
|
+
## Which reference to read
|
|
35
|
+
|
|
36
|
+
Keep this file in context and load a reference only for the detail.
|
|
37
|
+
|
|
38
|
+
| Work | Read |
|
|
39
|
+
| --------------------------------------------------------------------------- | -------------------------------------- |
|
|
40
|
+
| Models, APIs, hooks, migrations, users, filters, file managers, bulk, OAuth | `docs/agents/fastapi-rtk/backend.md` |
|
|
41
|
+
| Provider setup, ApiProvider, hooks, DataGrid, forms, dialogs, i18n | `docs/agents/fastapi-rtk/frontend.md` |
|
|
42
|
+
| Nested or conditional schemas, a custom form input, the `customizer` slot | `docs/agents/fastapi-rtk/jsonforms.md` |
|
|
43
|
+
| Full-stack | the table above, then both |
|
|
44
|
+
| Docs look wrong AND the project pins an OLDER release than the stamp below | `docs/agents/fastapi-rtk/legacy.md` |
|
|
45
|
+
|
|
46
|
+
`legacy.md` is keyed by symptom and never loads on a current release.
|
|
47
|
+
|
|
48
|
+
Bootstrapping a new project starts with `fastapi-rtk create-app`, which scaffolds the backend from the fastapi-rtk-skeleton template; the backend reference covers the pieces it produces. In a pre-existing project, read a few of its real files (`app/apis.py`, `src/main.jsx`) rather than assuming a fresh layout, since the toolkit is flexible.
|
|
49
|
+
|
|
50
|
+
**Staleness rule.** The three references were verified claim by claim against fastapi-rtk source at v2.9.0 (2026-07-09). At v2.18.3 (2026-09-16) a narrower re-check re-resolved every `file:line` citation, every config key, every backend symbol named here, the published JS export list and the declared peer dependencies, and fixed the seven that had drifted; prose claims outside those classes still date from v2.9.0. On a NEWER release, treat any claim contradicting observed behavior as possibly stale: read the installed package's source before fighting the code, and fix the reference and its version stamp in the same session when it drifted.
|