fastapi-admin-kit 0.3.1__tar.gz → 0.3.2__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_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/PKG-INFO +3 -1
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/__init__.py +1 -1
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/admin_database.py +13 -8
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/builtin_models.py +2 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/core.py +10 -4
- fastapi_admin_kit-0.3.2/fastapi_admin_kit/audit/models.py +22 -0
- fastapi_admin_kit-0.3.2/fastapi_admin_kit/auth/models.py +45 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/backends/sqlalchemy.py +243 -4
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/__init__.py +1 -1
- fastapi_admin_kit-0.3.2/fastapi_admin_kit/cli/migrate.py +636 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/behavior.py +2 -0
- fastapi_admin_kit-0.3.2/fastapi_admin_kit/migrations/models.py +116 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/schemas/builtin.py +95 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/schemas/schema.py +3 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/dashboard.html +1 -6
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/sidebar.html +1 -11
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/pyproject.toml +2 -0
- fastapi_admin_kit-0.3.1/fastapi_admin_kit/audit/models.py +0 -51
- fastapi_admin_kit-0.3.1/fastapi_admin_kit/auth/models.py +0 -242
- fastapi_admin_kit-0.3.1/fastapi_admin_kit/cli/migrate.py +0 -266
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/.gitignore +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/CHANGELOG.md +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/LICENSE +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/README.md +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/actions/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/actions/base.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/actions/registry.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/admin_config.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/admin_router.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/admin_template.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/decorators.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/state.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/auth.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/crud.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/deps.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/roles.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/schema_generator.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/schemas.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/search.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/context.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/diff.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/event_bus.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/events.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/listener.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/logger.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/middleware.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/sqlalchemy_logger.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/backend.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/csrf.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/dependencies.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/hasher.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/identity.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/mixins.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/password.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/permissions.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/protocol.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/ratelimit.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/router.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/session.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/totp.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/types.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/views.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/backends/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/backends/protocols.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/__main__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/helpers.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/permissions.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/scaffold.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/user.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/audit.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/auth.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/database.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/nav.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/storage.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/theme.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/ui.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/dashboard/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/db.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/exceptions.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/base.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/csv.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/excel.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/registry.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/field_types.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/filters/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/filters/base.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/filters/registry.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/flash.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/form/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/form/pipeline.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/form/types.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inline.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inspection/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inspection/registry.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inspection/types.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inspection.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/modeladmin.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/models/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/models/base.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/nav.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/base.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/cursor.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/dynamic.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/offset.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/plugins/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/py.typed +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/registry/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/registry/core.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/registry/validation.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/registry.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/router.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/schemas/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/search_utils.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/css/admin.css +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/css/presets.css +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/css/tokens.css +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/css/variables.css +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/icons/heroicons.svg +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/js/admin.js +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/js/htmx-config.js +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/storage/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/storage/base.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/storage/local.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/base.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/macros/form_fields.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/macros/icons.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/macros/table.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/macros/widgets.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/2fa/setup.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/2fa/verify.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/403.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/audit_detail.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/audit_log.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/detail.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/form.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/list.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/login.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/profile/password.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/profile/profile.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/role_form.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/roles/form.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/roles/list.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/roles.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/search.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/settings/theme.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/users/form.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/users/list.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/_inline_edit_row.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/command_palette.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/field_wrapper.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/flash_messages.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/head.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/head_minimal.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/inline_edit_form.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/inline_stacked.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/inline_tabular.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/list_table.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/mobile_backdrop.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/pagination.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/perm_select_widget.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/permission_widget.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/scripts.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/topbar.html +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/types.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/ui/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/ui/types.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/validation.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/audit.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/bulk.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/class_views.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/context.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/dashboard.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/delete.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/extra.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/factory.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/file_handler.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/form.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/list.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/list_context.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/model_saver.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/profile.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/protocols.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/renderers.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/roles.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/search.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/settings.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/sidebar.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/totp.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/users.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/__init__.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/base.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/inputs.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/registry.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/relation.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/resolver.py +0 -0
- {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/mkdocs.yml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fastapi-admin-kit
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Drop-in admin panel for FastAPI + SQLAlchemy + SQLModel apps
|
|
5
5
|
Project-URL: Homepage, https://github.com/borhanst/fastapi-admin-kit
|
|
6
6
|
Project-URL: Documentation, https://borhanst.github.io/fastapi-admin-kit/
|
|
@@ -31,6 +31,8 @@ Requires-Dist: jinja2>=3.1.0
|
|
|
31
31
|
Requires-Dist: pre-commit>=4.6.0
|
|
32
32
|
Requires-Dist: python-multipart>=0.0.9
|
|
33
33
|
Requires-Dist: sqlalchemy>=2.0.0
|
|
34
|
+
Provides-Extra: alembic
|
|
35
|
+
Requires-Dist: alembic>=1.13.0; extra == 'alembic'
|
|
34
36
|
Provides-Extra: dev
|
|
35
37
|
Requires-Dist: hatch>=1.13.0; extra == 'dev'
|
|
36
38
|
Requires-Dist: httpx>=0.27.0; extra == 'dev'
|
{fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/admin_database.py
RENAMED
|
@@ -33,10 +33,12 @@ class AdminDatabase:
|
|
|
33
33
|
engine: Any | None = None,
|
|
34
34
|
base: Any | None = None,
|
|
35
35
|
database_config: DatabaseConfig | None = None,
|
|
36
|
+
use_alembic: bool = False,
|
|
36
37
|
):
|
|
37
38
|
self.engine = engine
|
|
38
39
|
self.base = base
|
|
39
40
|
self.database_config = database_config
|
|
41
|
+
self.use_alembic = use_alembic
|
|
40
42
|
self._backend = SqlAlchemyDatabaseBackend(
|
|
41
43
|
admin_database=self, database_config=database_config
|
|
42
44
|
)
|
|
@@ -48,16 +50,19 @@ class AdminDatabase:
|
|
|
48
50
|
return self.engine
|
|
49
51
|
|
|
50
52
|
async def _create_tables(self) -> None:
|
|
51
|
-
"""Create all admin database tables (async-safe).
|
|
52
|
-
from sqlalchemy.ext.asyncio import AsyncEngine
|
|
53
|
+
"""Create all admin database tables (async-safe).
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
If ``use_alembic=True`` (production mode), this method does nothing
|
|
56
|
+
and expects Alembic to manage the schema via migrations.
|
|
57
|
+
"""
|
|
58
|
+
if self.use_alembic:
|
|
59
|
+
logger.info("use_alembic=True: skipping create_all; schema managed by Alembic")
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
from sqlalchemy.ext.asyncio import AsyncEngine
|
|
57
63
|
|
|
58
64
|
# Import models to register them with metadata
|
|
59
|
-
from fastapi_admin_kit.
|
|
60
|
-
from fastapi_admin_kit.models.base import Base as AdminBase
|
|
65
|
+
from fastapi_admin_kit.migrations.models import Base as AdminBase
|
|
61
66
|
|
|
62
67
|
if isinstance(self.engine, AsyncEngine):
|
|
63
68
|
# Async engine - use run_sync
|
|
@@ -166,7 +171,7 @@ class AdminDatabase:
|
|
|
166
171
|
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
|
|
167
172
|
from sqlalchemy.orm import Session, sessionmaker
|
|
168
173
|
|
|
169
|
-
from fastapi_admin_kit.
|
|
174
|
+
from fastapi_admin_kit.migrations.models import Permission, Role
|
|
170
175
|
|
|
171
176
|
is_async = isinstance(self.engine, AsyncEngine)
|
|
172
177
|
|
{fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/builtin_models.py
RENAMED
|
@@ -170,6 +170,8 @@ class UserAdmin(ModelAdmin):
|
|
|
170
170
|
class RoleAdmin(ModelAdmin):
|
|
171
171
|
tag = "admin"
|
|
172
172
|
icon = "shield-check"
|
|
173
|
+
verbose_name = "Admin Role"
|
|
174
|
+
verbose_name_plural = "Admin Roles"
|
|
173
175
|
list_display = ["id", "name", "description"]
|
|
174
176
|
search_fields = ["name"]
|
|
175
177
|
exclude = ["users"]
|
|
@@ -267,7 +267,12 @@ class Admin:
|
|
|
267
267
|
)
|
|
268
268
|
|
|
269
269
|
if database is None:
|
|
270
|
-
database = AdminDatabase(
|
|
270
|
+
database = AdminDatabase(
|
|
271
|
+
engine=engine,
|
|
272
|
+
base=base,
|
|
273
|
+
database_config=database_config,
|
|
274
|
+
use_alembic=config.behavior.use_alembic,
|
|
275
|
+
)
|
|
271
276
|
|
|
272
277
|
if router is None:
|
|
273
278
|
router = AdminRouter(
|
|
@@ -541,7 +546,8 @@ class Admin:
|
|
|
541
546
|
# 8.2 Apply skip_models — mark listed models to hide from admin
|
|
542
547
|
skip_models = self.config.behavior.skip_models
|
|
543
548
|
# Built-in internal models are always hidden from admin
|
|
544
|
-
|
|
549
|
+
# Note: model class names match table names (e.g., admin_refresh_tokens)
|
|
550
|
+
default_skip = {"admin_refresh_tokens", "admin_user_permissions", "admin_user_totp"}
|
|
545
551
|
all_skip = default_skip | skip_models
|
|
546
552
|
skip_lower = {s.lower() for s in all_skip}
|
|
547
553
|
for registered in self.registry.all():
|
|
@@ -991,8 +997,8 @@ class Admin:
|
|
|
991
997
|
RoleAdmin,
|
|
992
998
|
UserAdmin,
|
|
993
999
|
)
|
|
994
|
-
from fastapi_admin_kit.
|
|
995
|
-
|
|
1000
|
+
from fastapi_admin_kit.migrations.models import (
|
|
1001
|
+
AuditLog,
|
|
996
1002
|
LoginAttempt,
|
|
997
1003
|
Permission,
|
|
998
1004
|
Role,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""SQLAlchemy model for the admin audit log.
|
|
2
|
+
|
|
3
|
+
.. deprecated:: 2.1.0
|
|
4
|
+
This module is deprecated. Use ``fastapi_admin_kit.migrations.models`` instead,
|
|
5
|
+
which materializes models from schemas for both runtime and Alembic migrations.
|
|
6
|
+
This module will be removed in v3.0.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import warnings
|
|
12
|
+
|
|
13
|
+
from fastapi_admin_kit.migrations.models import AuditLog
|
|
14
|
+
|
|
15
|
+
warnings.warn(
|
|
16
|
+
"fastapi_admin_kit.audit.models is deprecated and will be removed in v3.0. "
|
|
17
|
+
"Use fastapi_admin_kit.migrations.models instead.",
|
|
18
|
+
DeprecationWarning,
|
|
19
|
+
stacklevel=2,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = ["AuditLog"]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""SQLAlchemy models for admin auth: roles, users, permissions.
|
|
2
|
+
|
|
3
|
+
.. deprecated:: 2.1.0
|
|
4
|
+
This module is deprecated. Use ``fastapi_admin_kit.migrations.models`` instead,
|
|
5
|
+
which materializes models from schemas for both runtime and Alembic migrations.
|
|
6
|
+
This module will be removed in v3.0.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import warnings
|
|
12
|
+
|
|
13
|
+
from fastapi_admin_kit.auth.mixins import AuthModelMixin
|
|
14
|
+
from fastapi_admin_kit.migrations.models import (
|
|
15
|
+
LoginAttempt,
|
|
16
|
+
Permission,
|
|
17
|
+
RefreshToken,
|
|
18
|
+
Role,
|
|
19
|
+
User,
|
|
20
|
+
UserPermission,
|
|
21
|
+
UserTOTP,
|
|
22
|
+
admin_role_permissions,
|
|
23
|
+
admin_user_roles,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
warnings.warn(
|
|
27
|
+
"fastapi_admin_kit.auth.models is deprecated and will be removed in v3.0. "
|
|
28
|
+
"Use fastapi_admin_kit.migrations.models instead.",
|
|
29
|
+
DeprecationWarning,
|
|
30
|
+
stacklevel=2,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Re-export the mixin
|
|
34
|
+
__all__ = [
|
|
35
|
+
"AuthModelMixin",
|
|
36
|
+
"User",
|
|
37
|
+
"Role",
|
|
38
|
+
"Permission",
|
|
39
|
+
"UserPermission",
|
|
40
|
+
"RefreshToken",
|
|
41
|
+
"UserTOTP",
|
|
42
|
+
"LoginAttempt",
|
|
43
|
+
"admin_user_roles",
|
|
44
|
+
"admin_role_permissions",
|
|
45
|
+
]
|
{fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/backends/sqlalchemy.py
RENAMED
|
@@ -561,11 +561,13 @@ class SqlAlchemyDatabaseBackend:
|
|
|
561
561
|
Column,
|
|
562
562
|
DateTime,
|
|
563
563
|
Float,
|
|
564
|
+
ForeignKey,
|
|
565
|
+
Index,
|
|
564
566
|
Integer,
|
|
565
567
|
String,
|
|
566
568
|
Text,
|
|
567
569
|
)
|
|
568
|
-
from sqlalchemy.
|
|
570
|
+
from sqlalchemy.orm import relationship
|
|
569
571
|
from sqlalchemy.sql import func
|
|
570
572
|
|
|
571
573
|
from fastapi_admin_kit.schemas.schema import Schema as SchemaType
|
|
@@ -580,6 +582,32 @@ class SqlAlchemyDatabaseBackend:
|
|
|
580
582
|
|
|
581
583
|
base = Base
|
|
582
584
|
|
|
585
|
+
# Reuse an already-materialized class for this table instead of creating
|
|
586
|
+
# a duplicate mapper. Duplicate mappers for the same table share a
|
|
587
|
+
# declarative registry entry, so lazy relationship/back_populates
|
|
588
|
+
# resolution can point at a stale copy and fail mapper configuration.
|
|
589
|
+
for mapper in list(base.registry.mappers):
|
|
590
|
+
table = mapper.local_table
|
|
591
|
+
if table is not None and table.name == schema.table_name:
|
|
592
|
+
return mapper.class_
|
|
593
|
+
|
|
594
|
+
# Cross-dialect JSON type
|
|
595
|
+
from sqlalchemy import types
|
|
596
|
+
|
|
597
|
+
class JSON(types.TypeDecorator):
|
|
598
|
+
impl = Text
|
|
599
|
+
cache_ok = True
|
|
600
|
+
|
|
601
|
+
def process_bind_param(self, value, dialect):
|
|
602
|
+
import json
|
|
603
|
+
|
|
604
|
+
return json.dumps(value) if value is not None else None
|
|
605
|
+
|
|
606
|
+
def process_result_value(self, value, dialect):
|
|
607
|
+
import json
|
|
608
|
+
|
|
609
|
+
return json.loads(value) if value is not None else None
|
|
610
|
+
|
|
583
611
|
type_map: dict[str, type] = {
|
|
584
612
|
"integer": Integer,
|
|
585
613
|
"string": String,
|
|
@@ -587,7 +615,7 @@ class SqlAlchemyDatabaseBackend:
|
|
|
587
615
|
"boolean": Boolean,
|
|
588
616
|
"datetime": DateTime(timezone=True),
|
|
589
617
|
"float": Float,
|
|
590
|
-
"json":
|
|
618
|
+
"json": JSON,
|
|
591
619
|
}
|
|
592
620
|
|
|
593
621
|
columns: list[Column] = []
|
|
@@ -596,6 +624,12 @@ class SqlAlchemyDatabaseBackend:
|
|
|
596
624
|
existing_table = base.metadata.tables.get(schema.table_name)
|
|
597
625
|
if existing_table is not None:
|
|
598
626
|
existing_cols = {c.name: c for c in existing_table.columns}
|
|
627
|
+
|
|
628
|
+
# Collect many_to_one target tables to know which columns need ForeignKey
|
|
629
|
+
many_to_one_targets = {
|
|
630
|
+
rel.target: rel.name for rel in schema.relations if rel.type == "many_to_one"
|
|
631
|
+
}
|
|
632
|
+
|
|
599
633
|
for f in schema.fields:
|
|
600
634
|
sa_type = type_map.get(f.type, String)
|
|
601
635
|
|
|
@@ -624,18 +658,223 @@ class SqlAlchemyDatabaseBackend:
|
|
|
624
658
|
if existing_col is None or not existing_col.index:
|
|
625
659
|
kwargs["index"] = True
|
|
626
660
|
|
|
627
|
-
|
|
661
|
+
# Add ForeignKey for many_to_one relationship columns
|
|
662
|
+
# The FK column is typically named {target}_id where target is the table name
|
|
663
|
+
# But we need to handle cases like admin_users -> user_id (not admin_user_id)
|
|
664
|
+
fk_target = None
|
|
665
|
+
for target_table, rel_name in many_to_one_targets.items():
|
|
666
|
+
# Try common naming patterns for the FK column
|
|
667
|
+
# Strip common prefixes like "admin_" from table name
|
|
668
|
+
base_name = target_table
|
|
669
|
+
if base_name.startswith("admin_"):
|
|
670
|
+
base_name = base_name[6:] # Remove "admin_"
|
|
671
|
+
|
|
672
|
+
possible_names = [
|
|
673
|
+
f"{base_name.rstrip('s')}_id", # e.g., admin_users -> user_id
|
|
674
|
+
f"{base_name}_id", # e.g., admin_users -> users_id
|
|
675
|
+
f"{target_table.rstrip('s')}_id", # e.g., admin_users -> admin_user_id
|
|
676
|
+
f"{target_table}_id", # e.g., admin_users -> admin_users_id
|
|
677
|
+
]
|
|
678
|
+
for possible_name in possible_names:
|
|
679
|
+
if f.name == possible_name:
|
|
680
|
+
fk_target = target_table
|
|
681
|
+
break
|
|
682
|
+
if fk_target:
|
|
683
|
+
break
|
|
684
|
+
|
|
685
|
+
# Build column with ForeignKey if needed
|
|
686
|
+
if fk_target:
|
|
687
|
+
from sqlalchemy import ForeignKey
|
|
688
|
+
|
|
689
|
+
# Use string-based FK to allow target table to not exist yet
|
|
690
|
+
columns.append(
|
|
691
|
+
Column(f.name, sa_type, ForeignKey(f"{fk_target}.id", use_alter=True), **kwargs)
|
|
692
|
+
)
|
|
693
|
+
else:
|
|
694
|
+
columns.append(Column(f.name, sa_type, **kwargs))
|
|
695
|
+
|
|
696
|
+
# Build table args with indexes from schema
|
|
697
|
+
# SQLAlchemy expects __table_args__ as tuple where last element is dict for options
|
|
698
|
+
table_args_list: list[Any] = []
|
|
699
|
+
|
|
700
|
+
# Check if table already exists in metadata (to avoid recreating indexes)
|
|
701
|
+
existing_table = None
|
|
702
|
+
if base is not None and hasattr(base, "metadata"):
|
|
703
|
+
existing_table = base.metadata.tables.get(schema.table_name)
|
|
704
|
+
|
|
705
|
+
if hasattr(schema, "indexes") and schema.indexes and existing_table is None:
|
|
706
|
+
for idx_def in schema.indexes:
|
|
707
|
+
if isinstance(idx_def, dict):
|
|
708
|
+
columns_list = idx_def.get("columns", [])
|
|
709
|
+
name = idx_def.get("name")
|
|
710
|
+
unique = idx_def.get("unique", False)
|
|
711
|
+
if columns_list:
|
|
712
|
+
table_args_list.append(Index(name, *columns_list, unique=unique))
|
|
713
|
+
table_args_list.append({"extend_existing": True})
|
|
714
|
+
table_args = tuple(table_args_list)
|
|
628
715
|
|
|
629
716
|
# Build the model class dynamically
|
|
630
717
|
table_name = schema.table_name
|
|
631
718
|
model_attrs: dict[str, Any] = {
|
|
632
719
|
"__tablename__": table_name,
|
|
633
|
-
"__table_args__":
|
|
720
|
+
"__table_args__": table_args,
|
|
634
721
|
}
|
|
635
722
|
for col in columns:
|
|
636
723
|
model_attrs[col.key] = col
|
|
637
724
|
|
|
725
|
+
# Process relationships from schema (after model_attrs is defined)
|
|
726
|
+
for rel in schema.relations:
|
|
727
|
+
if rel.type == "many_to_many" and rel.through:
|
|
728
|
+
# Many-to-many relationship using a junction table
|
|
729
|
+
# Resolve the through table from metadata if it's a string and available
|
|
730
|
+
secondary = rel.through
|
|
731
|
+
if isinstance(secondary, str) and base is not None and hasattr(base, "metadata"):
|
|
732
|
+
table = base.metadata.tables.get(secondary)
|
|
733
|
+
if table is not None:
|
|
734
|
+
secondary = table
|
|
735
|
+
# Pass the table (or string name) - SQLAlchemy will resolve it at mapper config time
|
|
736
|
+
model_attrs[rel.name] = relationship(
|
|
737
|
+
rel.target,
|
|
738
|
+
secondary=secondary,
|
|
739
|
+
back_populates=rel.back_populates,
|
|
740
|
+
)
|
|
741
|
+
elif rel.type == "one_to_many":
|
|
742
|
+
# One-to-many: foreign key is on the target table
|
|
743
|
+
# Use same naming convention as many_to_one: strip "admin_" prefix and singularize
|
|
744
|
+
base_name = table_name
|
|
745
|
+
if base_name.startswith("admin_"):
|
|
746
|
+
base_name = base_name[6:] # Remove "admin_"
|
|
747
|
+
# Singularize: users -> user, roles -> role, etc.
|
|
748
|
+
if base_name.endswith("s"):
|
|
749
|
+
base_name = base_name[:-1]
|
|
750
|
+
fk_col_name = f"{base_name}_id"
|
|
751
|
+
model_attrs[rel.name] = relationship(
|
|
752
|
+
rel.target,
|
|
753
|
+
back_populates=rel.back_populates,
|
|
754
|
+
foreign_keys=f"[{rel.target}.{fk_col_name}]",
|
|
755
|
+
)
|
|
756
|
+
elif rel.type == "many_to_one":
|
|
757
|
+
# Many-to-one: foreign key is on this table
|
|
758
|
+
# Use smarter FK column naming that handles prefixes like "admin_"
|
|
759
|
+
target_table = rel.target
|
|
760
|
+
base_name = target_table
|
|
761
|
+
if base_name.startswith("admin_"):
|
|
762
|
+
base_name = base_name[6:] # Remove "admin_"
|
|
763
|
+
|
|
764
|
+
possible_names = [
|
|
765
|
+
f"{base_name.rstrip('s')}_id", # e.g., admin_users -> user_id
|
|
766
|
+
f"{base_name}_id", # e.g., admin_users -> users_id
|
|
767
|
+
f"{target_table.rstrip('s')}_id", # e.g., admin_users -> admin_user_id
|
|
768
|
+
f"{target_table}_id", # e.g., admin_users -> admin_users_id
|
|
769
|
+
]
|
|
770
|
+
fk_col_name = None
|
|
771
|
+
for possible_name in possible_names:
|
|
772
|
+
if possible_name in model_attrs:
|
|
773
|
+
fk_col_name = possible_name
|
|
774
|
+
break
|
|
775
|
+
|
|
776
|
+
if fk_col_name:
|
|
777
|
+
model_attrs[rel.name] = relationship(
|
|
778
|
+
rel.target,
|
|
779
|
+
back_populates=rel.back_populates,
|
|
780
|
+
foreign_keys=model_attrs[fk_col_name],
|
|
781
|
+
)
|
|
782
|
+
|
|
638
783
|
model_class = type(table_name, (base,), model_attrs)
|
|
784
|
+
|
|
785
|
+
# Add AuthModelMixin methods to User model if this is the User schema
|
|
786
|
+
if schema.table_name == "admin_users":
|
|
787
|
+
from fastapi_admin_kit.auth.password import password_manager
|
|
788
|
+
|
|
789
|
+
# Add role_ids property
|
|
790
|
+
@property
|
|
791
|
+
def role_ids(self) -> list[int]:
|
|
792
|
+
roles = getattr(self, "roles", None)
|
|
793
|
+
if roles is None:
|
|
794
|
+
return []
|
|
795
|
+
return [r.id for r in roles]
|
|
796
|
+
|
|
797
|
+
model_class.role_ids = role_ids
|
|
798
|
+
|
|
799
|
+
# Add verify_password method
|
|
800
|
+
def verify_password(self, password: str) -> bool:
|
|
801
|
+
return password_manager.verify(password, self.hashed_password)
|
|
802
|
+
|
|
803
|
+
model_class.verify_password = verify_password
|
|
804
|
+
|
|
805
|
+
# Add hash_password classmethod
|
|
806
|
+
@classmethod
|
|
807
|
+
def hash_password(cls, password: str) -> str:
|
|
808
|
+
return password_manager.hash(password)
|
|
809
|
+
|
|
810
|
+
model_class.hash_password = hash_password
|
|
811
|
+
|
|
812
|
+
# Add set_hasher classmethod
|
|
813
|
+
@classmethod
|
|
814
|
+
def set_hasher(cls, hasher: type) -> None:
|
|
815
|
+
cls._hasher = hasher
|
|
816
|
+
|
|
817
|
+
model_class.set_hasher = set_hasher
|
|
818
|
+
|
|
819
|
+
# Initialize _hasher attribute
|
|
820
|
+
model_class._hasher = None
|
|
821
|
+
|
|
822
|
+
# Add has_perm method
|
|
823
|
+
async def has_perm(self, perm_name: str, session) -> bool:
|
|
824
|
+
if self.is_superuser:
|
|
825
|
+
return True
|
|
826
|
+
|
|
827
|
+
from sqlalchemy import select
|
|
828
|
+
|
|
829
|
+
from fastapi_admin_kit.migrations.models import (
|
|
830
|
+
Permission,
|
|
831
|
+
UserPermission,
|
|
832
|
+
admin_role_permissions,
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
# Parse perm_name -> (table_name, action)
|
|
836
|
+
if ":" in perm_name:
|
|
837
|
+
table_name, action = perm_name.rsplit(":", 1)
|
|
838
|
+
else:
|
|
839
|
+
parts = perm_name.rsplit("_", 1)
|
|
840
|
+
if len(parts) != 2:
|
|
841
|
+
return False
|
|
842
|
+
table_name, action = parts
|
|
843
|
+
|
|
844
|
+
attr = f"can_{action}"
|
|
845
|
+
if attr not in ("can_view", "can_create", "can_edit", "can_delete"):
|
|
846
|
+
return False
|
|
847
|
+
|
|
848
|
+
role_ids = self.role_ids
|
|
849
|
+
if not role_ids and not self.id:
|
|
850
|
+
return False
|
|
851
|
+
|
|
852
|
+
if role_ids:
|
|
853
|
+
result = await session.execute(
|
|
854
|
+
select(Permission)
|
|
855
|
+
.join(
|
|
856
|
+
admin_role_permissions,
|
|
857
|
+
Permission.id == admin_role_permissions.c.permission_id,
|
|
858
|
+
)
|
|
859
|
+
.where(admin_role_permissions.c.role_id.in_(role_ids))
|
|
860
|
+
)
|
|
861
|
+
for perm in result.scalars():
|
|
862
|
+
if perm.table_name == table_name and getattr(perm, attr, False):
|
|
863
|
+
return True
|
|
864
|
+
|
|
865
|
+
result = await session.execute(
|
|
866
|
+
select(Permission)
|
|
867
|
+
.join(UserPermission, UserPermission.permission_id == Permission.id)
|
|
868
|
+
.where(UserPermission.user_id == self.id)
|
|
869
|
+
)
|
|
870
|
+
for perm in result.scalars():
|
|
871
|
+
if perm.table_name == table_name and getattr(perm, attr, False):
|
|
872
|
+
return True
|
|
873
|
+
|
|
874
|
+
return False
|
|
875
|
+
|
|
876
|
+
model_class.has_perm = has_perm
|
|
877
|
+
|
|
639
878
|
return model_class
|
|
640
879
|
|
|
641
880
|
|
|
@@ -73,7 +73,7 @@ def main() -> None:
|
|
|
73
73
|
handle_user_command(args)
|
|
74
74
|
elif args.command in ("createpermissions", "createadminpermissions", "deletepermissions"):
|
|
75
75
|
handle_permission_command(args)
|
|
76
|
-
elif args.command in ("migrate", "migrate-permissions"):
|
|
76
|
+
elif args.command in ("migrate", "migrate-permissions", "migrate-alembic", "init-alembic"):
|
|
77
77
|
handle_migrate_command(args)
|
|
78
78
|
elif args.command == "init":
|
|
79
79
|
_init_project(args)
|