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.
Files changed (205) hide show
  1. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/PKG-INFO +3 -1
  2. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/__init__.py +1 -1
  3. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/admin_database.py +13 -8
  4. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/builtin_models.py +2 -0
  5. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/core.py +10 -4
  6. fastapi_admin_kit-0.3.2/fastapi_admin_kit/audit/models.py +22 -0
  7. fastapi_admin_kit-0.3.2/fastapi_admin_kit/auth/models.py +45 -0
  8. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/backends/sqlalchemy.py +243 -4
  9. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/__init__.py +1 -1
  10. fastapi_admin_kit-0.3.2/fastapi_admin_kit/cli/migrate.py +636 -0
  11. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/behavior.py +2 -0
  12. fastapi_admin_kit-0.3.2/fastapi_admin_kit/migrations/models.py +116 -0
  13. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/schemas/builtin.py +95 -0
  14. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/schemas/schema.py +3 -0
  15. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/dashboard.html +1 -6
  16. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/sidebar.html +1 -11
  17. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/pyproject.toml +2 -0
  18. fastapi_admin_kit-0.3.1/fastapi_admin_kit/audit/models.py +0 -51
  19. fastapi_admin_kit-0.3.1/fastapi_admin_kit/auth/models.py +0 -242
  20. fastapi_admin_kit-0.3.1/fastapi_admin_kit/cli/migrate.py +0 -266
  21. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/.gitignore +0 -0
  22. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/CHANGELOG.md +0 -0
  23. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/LICENSE +0 -0
  24. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/README.md +0 -0
  25. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/actions/__init__.py +0 -0
  26. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/actions/base.py +0 -0
  27. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/actions/registry.py +0 -0
  28. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/__init__.py +0 -0
  29. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/admin_config.py +0 -0
  30. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/admin_router.py +0 -0
  31. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/admin_template.py +0 -0
  32. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/decorators.py +0 -0
  33. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/admin/state.py +0 -0
  34. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/__init__.py +0 -0
  35. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/auth.py +0 -0
  36. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/crud.py +0 -0
  37. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/deps.py +0 -0
  38. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/roles.py +0 -0
  39. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/schema_generator.py +0 -0
  40. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/schemas.py +0 -0
  41. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/api/search.py +0 -0
  42. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/__init__.py +0 -0
  43. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/context.py +0 -0
  44. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/diff.py +0 -0
  45. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/event_bus.py +0 -0
  46. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/events.py +0 -0
  47. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/listener.py +0 -0
  48. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/logger.py +0 -0
  49. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/middleware.py +0 -0
  50. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/audit/sqlalchemy_logger.py +0 -0
  51. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/__init__.py +0 -0
  52. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/backend.py +0 -0
  53. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/csrf.py +0 -0
  54. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/dependencies.py +0 -0
  55. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/hasher.py +0 -0
  56. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/identity.py +0 -0
  57. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/mixins.py +0 -0
  58. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/password.py +0 -0
  59. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/permissions.py +0 -0
  60. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/protocol.py +0 -0
  61. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/ratelimit.py +0 -0
  62. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/router.py +0 -0
  63. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/session.py +0 -0
  64. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/totp.py +0 -0
  65. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/types.py +0 -0
  66. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/auth/views.py +0 -0
  67. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/backends/__init__.py +0 -0
  68. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/backends/protocols.py +0 -0
  69. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/__main__.py +0 -0
  70. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/helpers.py +0 -0
  71. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/permissions.py +0 -0
  72. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/scaffold.py +0 -0
  73. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/cli/user.py +0 -0
  74. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/__init__.py +0 -0
  75. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/audit.py +0 -0
  76. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/auth.py +0 -0
  77. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/database.py +0 -0
  78. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/nav.py +0 -0
  79. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/storage.py +0 -0
  80. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/theme.py +0 -0
  81. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/config/ui.py +0 -0
  82. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/dashboard/__init__.py +0 -0
  83. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/db.py +0 -0
  84. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/exceptions.py +0 -0
  85. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/__init__.py +0 -0
  86. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/base.py +0 -0
  87. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/csv.py +0 -0
  88. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/excel.py +0 -0
  89. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/export_import/registry.py +0 -0
  90. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/field_types.py +0 -0
  91. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/filters/__init__.py +0 -0
  92. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/filters/base.py +0 -0
  93. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/filters/registry.py +0 -0
  94. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/flash.py +0 -0
  95. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/form/__init__.py +0 -0
  96. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/form/pipeline.py +0 -0
  97. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/form/types.py +0 -0
  98. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inline.py +0 -0
  99. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inspection/__init__.py +0 -0
  100. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inspection/registry.py +0 -0
  101. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inspection/types.py +0 -0
  102. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/inspection.py +0 -0
  103. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/modeladmin.py +0 -0
  104. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/models/__init__.py +0 -0
  105. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/models/base.py +0 -0
  106. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/nav.py +0 -0
  107. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/__init__.py +0 -0
  108. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/base.py +0 -0
  109. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/cursor.py +0 -0
  110. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/dynamic.py +0 -0
  111. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/pagination/offset.py +0 -0
  112. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/plugins/__init__.py +0 -0
  113. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/py.typed +0 -0
  114. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/registry/__init__.py +0 -0
  115. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/registry/core.py +0 -0
  116. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/registry/validation.py +0 -0
  117. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/registry.py +0 -0
  118. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/router.py +0 -0
  119. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/schemas/__init__.py +0 -0
  120. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/search_utils.py +0 -0
  121. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/css/admin.css +0 -0
  122. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/css/presets.css +0 -0
  123. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/css/tokens.css +0 -0
  124. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/css/variables.css +0 -0
  125. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/icons/heroicons.svg +0 -0
  126. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/js/admin.js +0 -0
  127. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/static/js/htmx-config.js +0 -0
  128. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/storage/__init__.py +0 -0
  129. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/storage/base.py +0 -0
  130. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/storage/local.py +0 -0
  131. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/base.html +0 -0
  132. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/macros/form_fields.html +0 -0
  133. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/macros/icons.html +0 -0
  134. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/macros/table.html +0 -0
  135. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/macros/widgets.html +0 -0
  136. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/2fa/setup.html +0 -0
  137. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/2fa/verify.html +0 -0
  138. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/403.html +0 -0
  139. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/audit_detail.html +0 -0
  140. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/audit_log.html +0 -0
  141. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/detail.html +0 -0
  142. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/form.html +0 -0
  143. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/list.html +0 -0
  144. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/login.html +0 -0
  145. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/profile/password.html +0 -0
  146. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/profile/profile.html +0 -0
  147. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/role_form.html +0 -0
  148. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/roles/form.html +0 -0
  149. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/roles/list.html +0 -0
  150. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/roles.html +0 -0
  151. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/search.html +0 -0
  152. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/settings/theme.html +0 -0
  153. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/users/form.html +0 -0
  154. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/pages/users/list.html +0 -0
  155. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/_inline_edit_row.html +0 -0
  156. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/command_palette.html +0 -0
  157. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/field_wrapper.html +0 -0
  158. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/flash_messages.html +0 -0
  159. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/head.html +0 -0
  160. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/head_minimal.html +0 -0
  161. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/inline_edit_form.html +0 -0
  162. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/inline_stacked.html +0 -0
  163. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/inline_tabular.html +0 -0
  164. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/list_table.html +0 -0
  165. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/mobile_backdrop.html +0 -0
  166. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/pagination.html +0 -0
  167. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/perm_select_widget.html +0 -0
  168. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/permission_widget.html +0 -0
  169. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/scripts.html +0 -0
  170. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/templates/partials/topbar.html +0 -0
  171. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/types.py +0 -0
  172. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/ui/__init__.py +0 -0
  173. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/ui/types.py +0 -0
  174. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/validation.py +0 -0
  175. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/__init__.py +0 -0
  176. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/audit.py +0 -0
  177. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/bulk.py +0 -0
  178. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/class_views.py +0 -0
  179. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/context.py +0 -0
  180. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/dashboard.py +0 -0
  181. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/delete.py +0 -0
  182. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/extra.py +0 -0
  183. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/factory.py +0 -0
  184. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/file_handler.py +0 -0
  185. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/form.py +0 -0
  186. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/list.py +0 -0
  187. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/list_context.py +0 -0
  188. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/model_saver.py +0 -0
  189. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/profile.py +0 -0
  190. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/protocols.py +0 -0
  191. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/renderers.py +0 -0
  192. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/roles.py +0 -0
  193. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/search.py +0 -0
  194. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/settings.py +0 -0
  195. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/sidebar.py +0 -0
  196. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/totp.py +0 -0
  197. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views/users.py +0 -0
  198. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/views.py +0 -0
  199. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/__init__.py +0 -0
  200. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/base.py +0 -0
  201. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/inputs.py +0 -0
  202. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/registry.py +0 -0
  203. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/relation.py +0 -0
  204. {fastapi_admin_kit-0.3.1 → fastapi_admin_kit-0.3.2}/fastapi_admin_kit/widgets/resolver.py +0 -0
  205. {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.1
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'
@@ -88,4 +88,4 @@ __all__ = [
88
88
  "CSVExport",
89
89
  "CSVImport",
90
90
  ]
91
- __version__ = "0.3.1"
91
+ __version__ = "0.3.2"
@@ -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
- from fastapi_admin_kit.audit import (
55
- models as _audit_models, # noqa: F401
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.auth import models as _auth_models # noqa: F401
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.auth.models import Permission, Role
174
+ from fastapi_admin_kit.migrations.models import Permission, Role
170
175
 
171
176
  is_async = isinstance(self.engine, AsyncEngine)
172
177
 
@@ -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(engine=engine, base=base, database_config=database_config)
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
- default_skip = {"RefreshToken", "UserPermission", "UserTOTP"}
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.audit.models import AuditLog
995
- from fastapi_admin_kit.auth.models import (
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
+ ]
@@ -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.dialects.postgresql import JSON as PG_JSON
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": PG_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
- columns.append(Column(f.name, sa_type, **kwargs))
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__": {"extend_existing": True},
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)