fastapi-admin-kit 0.2.1__tar.gz → 0.3.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.
Files changed (211) hide show
  1. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/PKG-INFO +3 -1
  2. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/__init__.py +14 -1
  3. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/admin/admin_database.py +11 -7
  4. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/admin/admin_template.py +54 -17
  5. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/admin/core.py +39 -9
  6. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/admin/state.py +4 -0
  7. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/context.py +33 -1
  8. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/diff.py +14 -24
  9. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/events.py +2 -2
  10. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/listener.py +15 -17
  11. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/logger.py +8 -0
  12. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/sqlalchemy_logger.py +6 -0
  13. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/__init__.py +16 -0
  14. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/backend.py +0 -20
  15. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/dependencies.py +28 -0
  16. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/hasher.py +10 -9
  17. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/mixins.py +21 -24
  18. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/models.py +2 -0
  19. fastapi_admin_kit-0.3.1/fastapi_admin_kit/auth/password.py +80 -0
  20. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/permissions.py +28 -3
  21. fastapi_admin_kit-0.3.1/fastapi_admin_kit/auth/protocol.py +72 -0
  22. fastapi_admin_kit-0.3.1/fastapi_admin_kit/auth/types.py +26 -0
  23. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/views.py +1 -1
  24. fastapi_admin_kit-0.3.1/fastapi_admin_kit/backends/__init__.py +63 -0
  25. fastapi_admin_kit-0.3.1/fastapi_admin_kit/backends/protocols.py +196 -0
  26. fastapi_admin_kit-0.3.1/fastapi_admin_kit/backends/sqlalchemy.py +678 -0
  27. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/cli/permissions.py +5 -4
  28. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/config/nav.py +2 -0
  29. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/db.py +51 -23
  30. fastapi_admin_kit-0.3.1/fastapi_admin_kit/export_import/__init__.py +28 -0
  31. fastapi_admin_kit-0.3.1/fastapi_admin_kit/export_import/base.py +387 -0
  32. fastapi_admin_kit-0.3.1/fastapi_admin_kit/export_import/csv.py +181 -0
  33. fastapi_admin_kit-0.3.1/fastapi_admin_kit/export_import/excel.py +193 -0
  34. fastapi_admin_kit-0.3.1/fastapi_admin_kit/export_import/registry.py +85 -0
  35. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/filters/__init__.py +12 -0
  36. fastapi_admin_kit-0.3.1/fastapi_admin_kit/filters/base.py +335 -0
  37. fastapi_admin_kit-0.3.1/fastapi_admin_kit/filters/registry.py +163 -0
  38. fastapi_admin_kit-0.3.1/fastapi_admin_kit/form/pipeline.py +440 -0
  39. fastapi_admin_kit-0.3.1/fastapi_admin_kit/form/types.py +129 -0
  40. fastapi_admin_kit-0.3.1/fastapi_admin_kit/inline.py +111 -0
  41. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/inspection/__init__.py +37 -58
  42. fastapi_admin_kit-0.3.1/fastapi_admin_kit/inspection/registry.py +144 -0
  43. fastapi_admin_kit-0.3.1/fastapi_admin_kit/inspection/types.py +37 -0
  44. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/inspection.py +1 -1
  45. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/modeladmin.py +131 -6
  46. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/pagination/base.py +14 -1
  47. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/pagination/cursor.py +25 -11
  48. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/pagination/dynamic.py +15 -6
  49. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/pagination/offset.py +15 -5
  50. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/router.py +205 -3
  51. fastapi_admin_kit-0.3.1/fastapi_admin_kit/schemas/__init__.py +37 -0
  52. fastapi_admin_kit-0.3.1/fastapi_admin_kit/schemas/builtin.py +164 -0
  53. fastapi_admin_kit-0.3.1/fastapi_admin_kit/schemas/schema.py +119 -0
  54. fastapi_admin_kit-0.3.1/fastapi_admin_kit/search_utils.py +108 -0
  55. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/static/css/admin.css +22 -0
  56. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/static/js/admin.js +117 -1
  57. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/macros/icons.html +1 -0
  58. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/form.html +23 -1
  59. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/list.html +86 -3
  60. fastapi_admin_kit-0.3.1/fastapi_admin_kit/templates/partials/inline_stacked.html +179 -0
  61. fastapi_admin_kit-0.3.1/fastapi_admin_kit/templates/partials/inline_tabular.html +180 -0
  62. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/list_table.html +14 -2
  63. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/sidebar.html +17 -5
  64. fastapi_admin_kit-0.3.1/fastapi_admin_kit/types.py +46 -0
  65. fastapi_admin_kit-0.3.1/fastapi_admin_kit/ui/__init__.py +1 -0
  66. fastapi_admin_kit-0.3.1/fastapi_admin_kit/ui/types.py +35 -0
  67. fastapi_admin_kit-0.3.1/fastapi_admin_kit/views/__init__.py +80 -0
  68. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/audit.py +3 -11
  69. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/class_views.py +448 -99
  70. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/context.py +11 -241
  71. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/factory.py +24 -25
  72. fastapi_admin_kit-0.3.1/fastapi_admin_kit/views/file_handler.py +96 -0
  73. fastapi_admin_kit-0.3.1/fastapi_admin_kit/views/form.py +89 -0
  74. fastapi_admin_kit-0.3.1/fastapi_admin_kit/views/list_context.py +459 -0
  75. fastapi_admin_kit-0.3.1/fastapi_admin_kit/views/model_saver.py +395 -0
  76. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/renderers.py +189 -247
  77. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/roles.py +9 -17
  78. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/sidebar.py +5 -2
  79. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/users.py +9 -17
  80. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views.py +1 -1
  81. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/widgets/resolver.py +1 -1
  82. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/pyproject.toml +1 -0
  83. fastapi_admin_kit-0.2.1/fastapi_admin_kit/auth/password.py +0 -37
  84. fastapi_admin_kit-0.2.1/fastapi_admin_kit/auth/protocol.py +0 -22
  85. fastapi_admin_kit-0.2.1/fastapi_admin_kit/filters/base.py +0 -170
  86. fastapi_admin_kit-0.2.1/fastapi_admin_kit/filters/registry.py +0 -62
  87. fastapi_admin_kit-0.2.1/fastapi_admin_kit/form/pipeline.py +0 -118
  88. fastapi_admin_kit-0.2.1/fastapi_admin_kit/inspection/registry.py +0 -249
  89. fastapi_admin_kit-0.2.1/fastapi_admin_kit/search_utils.py +0 -71
  90. fastapi_admin_kit-0.2.1/fastapi_admin_kit/types.py +0 -160
  91. fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/__init__.py +0 -64
  92. fastapi_admin_kit-0.2.1/fastapi_admin_kit/views/form.py +0 -161
  93. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/.gitignore +0 -0
  94. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/CHANGELOG.md +0 -0
  95. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/LICENSE +0 -0
  96. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/README.md +0 -0
  97. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/actions/__init__.py +0 -0
  98. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/actions/base.py +0 -0
  99. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/actions/registry.py +0 -0
  100. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/admin/__init__.py +0 -0
  101. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/admin/admin_config.py +0 -0
  102. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/admin/admin_router.py +0 -0
  103. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/admin/builtin_models.py +0 -0
  104. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/admin/decorators.py +0 -0
  105. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/api/__init__.py +0 -0
  106. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/api/auth.py +0 -0
  107. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/api/crud.py +0 -0
  108. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/api/deps.py +0 -0
  109. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/api/roles.py +0 -0
  110. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/api/schema_generator.py +0 -0
  111. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/api/schemas.py +0 -0
  112. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/api/search.py +0 -0
  113. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/__init__.py +0 -0
  114. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/event_bus.py +0 -0
  115. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/middleware.py +0 -0
  116. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/audit/models.py +0 -0
  117. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/csrf.py +0 -0
  118. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/identity.py +0 -0
  119. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/ratelimit.py +0 -0
  120. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/router.py +0 -0
  121. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/session.py +0 -0
  122. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/auth/totp.py +0 -0
  123. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/cli/__init__.py +0 -0
  124. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/cli/__main__.py +0 -0
  125. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/cli/helpers.py +0 -0
  126. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/cli/migrate.py +0 -0
  127. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/cli/scaffold.py +0 -0
  128. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/cli/user.py +0 -0
  129. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/config/__init__.py +0 -0
  130. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/config/audit.py +0 -0
  131. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/config/auth.py +0 -0
  132. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/config/behavior.py +0 -0
  133. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/config/database.py +0 -0
  134. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/config/storage.py +0 -0
  135. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/config/theme.py +0 -0
  136. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/config/ui.py +0 -0
  137. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/dashboard/__init__.py +0 -0
  138. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/exceptions.py +0 -0
  139. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/field_types.py +0 -0
  140. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/flash.py +0 -0
  141. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/form/__init__.py +0 -0
  142. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/models/__init__.py +0 -0
  143. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/models/base.py +0 -0
  144. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/nav.py +0 -0
  145. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/pagination/__init__.py +0 -0
  146. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/plugins/__init__.py +0 -0
  147. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/py.typed +0 -0
  148. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/registry/__init__.py +0 -0
  149. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/registry/core.py +0 -0
  150. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/registry/validation.py +0 -0
  151. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/registry.py +0 -0
  152. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/static/css/presets.css +0 -0
  153. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/static/css/tokens.css +0 -0
  154. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/static/css/variables.css +0 -0
  155. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/static/icons/heroicons.svg +0 -0
  156. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/static/js/htmx-config.js +0 -0
  157. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/storage/__init__.py +0 -0
  158. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/storage/base.py +0 -0
  159. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/storage/local.py +0 -0
  160. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/base.html +0 -0
  161. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/macros/form_fields.html +0 -0
  162. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/macros/table.html +0 -0
  163. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/macros/widgets.html +0 -0
  164. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/2fa/setup.html +0 -0
  165. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/2fa/verify.html +0 -0
  166. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/403.html +0 -0
  167. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/audit_detail.html +0 -0
  168. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/audit_log.html +0 -0
  169. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/dashboard.html +0 -0
  170. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/detail.html +0 -0
  171. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/login.html +0 -0
  172. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/profile/password.html +0 -0
  173. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/profile/profile.html +0 -0
  174. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/role_form.html +0 -0
  175. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/roles/form.html +0 -0
  176. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/roles/list.html +0 -0
  177. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/roles.html +0 -0
  178. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/search.html +0 -0
  179. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/settings/theme.html +0 -0
  180. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/users/form.html +0 -0
  181. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/pages/users/list.html +0 -0
  182. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/_inline_edit_row.html +0 -0
  183. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/command_palette.html +0 -0
  184. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/field_wrapper.html +0 -0
  185. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/flash_messages.html +0 -0
  186. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/head.html +0 -0
  187. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/head_minimal.html +0 -0
  188. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/inline_edit_form.html +0 -0
  189. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/mobile_backdrop.html +0 -0
  190. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/pagination.html +0 -0
  191. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/perm_select_widget.html +0 -0
  192. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/permission_widget.html +0 -0
  193. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/scripts.html +0 -0
  194. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/templates/partials/topbar.html +0 -0
  195. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/validation.py +0 -0
  196. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/bulk.py +0 -0
  197. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/dashboard.py +0 -0
  198. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/delete.py +0 -0
  199. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/extra.py +0 -0
  200. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/list.py +0 -0
  201. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/profile.py +0 -0
  202. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/protocols.py +0 -0
  203. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/search.py +0 -0
  204. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/settings.py +0 -0
  205. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/views/totp.py +0 -0
  206. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/widgets/__init__.py +0 -0
  207. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/widgets/base.py +0 -0
  208. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/widgets/inputs.py +0 -0
  209. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/widgets/registry.py +0 -0
  210. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/fastapi_admin_kit/widgets/relation.py +0 -0
  211. {fastapi_admin_kit-0.2.1 → fastapi_admin_kit-0.3.1}/mkdocs.yml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-admin-kit
3
- Version: 0.2.1
3
+ Version: 0.3.1
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/
@@ -44,6 +44,8 @@ Provides-Extra: docs
44
44
  Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
45
45
  Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
46
46
  Requires-Dist: mkdocstrings[python]>=0.25.0; extra == 'docs'
47
+ Provides-Extra: excel
48
+ Requires-Dist: openpyxl>=3.1.0; extra == 'excel'
47
49
  Provides-Extra: full
48
50
  Requires-Dist: pyjwt>=2.13.0; extra == 'full'
49
51
  Requires-Dist: uvicorn>=0.30.0; extra == 'full'
@@ -5,6 +5,8 @@ from fastapi_admin_kit.admin.decorators import column
5
5
  from fastapi_admin_kit.auth.mixins import AuthModelMixin
6
6
  from fastapi_admin_kit.config import DatabaseConfig, DatabaseType
7
7
  from fastapi_admin_kit.exceptions import ConfigError
8
+ from fastapi_admin_kit.export_import import CSVExport, CSVImport, ExportBase, ImportBase
9
+ from fastapi_admin_kit.inline import InlineModelAdmin, StackedInline, TabularInline
8
10
  from fastapi_admin_kit.nav import (
9
11
  BuiltNavGroup,
10
12
  BuiltNavItem,
@@ -22,6 +24,7 @@ from fastapi_admin_kit.types import (
22
24
  FieldsetContext,
23
25
  FieldsetSpec,
24
26
  FormContext,
27
+ InlineFormsetData,
25
28
  PermissionSet,
26
29
  RelationMeta,
27
30
  SeedRole,
@@ -64,6 +67,11 @@ __all__ = [
64
67
  "FieldsetContext",
65
68
  "FieldsetSpec",
66
69
  "FormContext",
70
+ "InlineFormsetData",
71
+ # Inline admin
72
+ "InlineModelAdmin",
73
+ "StackedInline",
74
+ "TabularInline",
67
75
  # View classes
68
76
  "BaseView",
69
77
  "ListView",
@@ -74,5 +82,10 @@ __all__ = [
74
82
  "SearchView",
75
83
  # Per-model assets
76
84
  "AdminExtra",
85
+ # Export/Import
86
+ "ExportBase",
87
+ "ImportBase",
88
+ "CSVExport",
89
+ "CSVImport",
77
90
  ]
78
- __version__ = "0.2.1"
91
+ __version__ = "0.3.1"
@@ -6,6 +6,7 @@ import logging
6
6
  import re
7
7
  from typing import Any
8
8
 
9
+ from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyDatabaseBackend
9
10
  from fastapi_admin_kit.config.database import DatabaseConfig
10
11
 
11
12
  logger = logging.getLogger(__name__)
@@ -21,7 +22,11 @@ def _validate_identifier(name: str, kind: str = "table") -> str:
21
22
 
22
23
 
23
24
  class AdminDatabase:
24
- """Handles database setup, table creation, and role seeding."""
25
+ """Handles database setup, table creation, and role seeding.
26
+
27
+ Delegates engine creation, table creation, and auto-migration to
28
+ :class:`SqlAlchemyDatabaseBackend`.
29
+ """
25
30
 
26
31
  def __init__(
27
32
  self,
@@ -32,11 +37,12 @@ class AdminDatabase:
32
37
  self.engine = engine
33
38
  self.base = base
34
39
  self.database_config = database_config
40
+ self._backend = SqlAlchemyDatabaseBackend(
41
+ admin_database=self, database_config=database_config
42
+ )
35
43
 
36
44
  def _ensure_engine(self) -> Any:
37
- """
38
- Create the async engine from ``database_config`` if no engine is set.
39
- """
45
+ """Create the async engine from ``database_config`` if no engine is set."""
40
46
  if self.engine is None and self.database_config is not None:
41
47
  self.engine = self.database_config.create_engine()
42
48
  return self.engine
@@ -108,9 +114,7 @@ class AdminDatabase:
108
114
  conn.execute(sql)
109
115
 
110
116
  def _auto_migrate(self, sync_conn: Any, metadata: Any) -> None:
111
- """
112
- Add missing columns to existing tables (sync, called via run_sync).
113
- """
117
+ """Add missing columns to existing tables (sync, called via run_sync)."""
114
118
  from sqlalchemy import inspect as sa_inspect
115
119
  from sqlalchemy import text
116
120
 
@@ -1,10 +1,13 @@
1
1
  """Admin template management and context building."""
2
2
 
3
+ import logging
3
4
  from typing import TYPE_CHECKING, Any
4
5
 
5
6
  if TYPE_CHECKING:
6
7
  pass
7
8
 
9
+ logger = logging.getLogger(__name__)
10
+
8
11
 
9
12
  class AdminTemplate:
10
13
  """Manages Jinja2 template environment and sidebar context."""
@@ -19,6 +22,7 @@ class AdminTemplate:
19
22
  dark_mode_default: bool = False,
20
23
  dashboard_permission: str | None = None,
21
24
  settings_permission: str | None = None,
25
+ sidebar_bottom_links: list[dict[str, str]] | None = None,
22
26
  ):
23
27
  self.title = title
24
28
  self.logo_url = logo_url
@@ -28,6 +32,7 @@ class AdminTemplate:
28
32
  self.dark_mode_default = dark_mode_default
29
33
  self.dashboard_permission = dashboard_permission
30
34
  self.settings_permission = settings_permission
35
+ self.sidebar_bottom_links: list[dict[str, str]] = sidebar_bottom_links or []
31
36
  self._nav_groups_built: list = []
32
37
 
33
38
  def _init_jinja(self, app: Any) -> None:
@@ -46,11 +51,11 @@ class AdminTemplate:
46
51
  jinja_env.env.filters["slugify"] = slugify
47
52
  app.state.admin_jinja_env = jinja_env
48
53
 
49
- def sidebar_template_kwargs(self, request: Any) -> dict[str, Any]:
54
+ async def sidebar_template_kwargs(self, request: Any) -> dict[str, Any]:
50
55
  """Thin wrapper — returns sidebar kwargs for TemplateResponse contexts."""
51
- return self.build_sidebar_context(request)
56
+ return await self.build_sidebar_context(request)
52
57
 
53
- def build_sidebar_context(
58
+ async def build_sidebar_context(
54
59
  self,
55
60
  request: Any,
56
61
  user: Any = None,
@@ -84,28 +89,34 @@ class AdminTemplate:
84
89
  permissions_map = {}
85
90
 
86
91
  if user and not is_superuser:
92
+ user_id = snapshot.get("id") if snapshot else getattr(user, "id", None)
87
93
  role_ids = (
88
94
  snapshot.get("role_ids", []) if snapshot else getattr(user, "role_ids", [])
89
95
  )
90
- if role_ids:
96
+ if role_ids or user_id is not None:
91
97
  try:
92
98
  from sqlalchemy import select
93
- from sqlalchemy.orm import Session
94
99
 
95
- from fastapi_admin_kit.auth.models import Permission, admin_role_permissions
100
+ from fastapi_admin_kit.auth.models import (
101
+ Permission,
102
+ UserPermission,
103
+ admin_role_permissions,
104
+ )
105
+ from fastapi_admin_kit.db import get_db_session
106
+
107
+ session = get_db_session(request)
96
108
 
97
- engine = request.app.state.admin_engine
98
- with Session(engine) as s:
99
- result = s.execute(
109
+ # Load permissions from all roles, merge with OR logic
110
+ if role_ids:
111
+ result = await session.execute(
100
112
  select(Permission)
101
113
  .join(
102
114
  admin_role_permissions,
103
115
  Permission.id == admin_role_permissions.c.permission_id,
104
116
  )
105
- .filter(admin_role_permissions.c.role_id.in_(role_ids))
117
+ .where(admin_role_permissions.c.role_id.in_(role_ids))
106
118
  )
107
- rows = result.scalars().all()
108
- for perm in rows:
119
+ for perm in result.scalars():
109
120
  if perm.table_name in permissions_map:
110
121
  existing = permissions_map[perm.table_name]
111
122
  permissions_map[perm.table_name] = PermissionSet(
@@ -121,10 +132,35 @@ class AdminTemplate:
121
132
  can_edit=perm.can_edit,
122
133
  can_delete=perm.can_delete,
123
134
  )
124
- except Exception as exc:
125
- import logging
126
135
 
127
- logging.getLogger(__name__).debug("Permission query failed: %s", exc)
136
+ # Load direct user permission overrides, merge on top
137
+ if user_id is not None:
138
+ result = await session.execute(
139
+ select(UserPermission, Permission)
140
+ .join(Permission, UserPermission.permission_id == Permission.id)
141
+ .where(UserPermission.user_id == user_id)
142
+ )
143
+ for up, perm in result:
144
+ table = perm.table_name
145
+ if table in permissions_map:
146
+ existing = permissions_map[table]
147
+ permissions_map[table] = PermissionSet(
148
+ can_view=existing.can_view or perm.can_view,
149
+ can_create=existing.can_create or perm.can_create,
150
+ can_edit=existing.can_edit or perm.can_edit,
151
+ can_delete=existing.can_delete or perm.can_delete,
152
+ )
153
+ else:
154
+ permissions_map[table] = PermissionSet(
155
+ can_view=perm.can_view,
156
+ can_create=perm.can_create,
157
+ can_edit=perm.can_edit,
158
+ can_delete=perm.can_delete,
159
+ )
160
+ except Exception as exc:
161
+ logger.warning(
162
+ "Permission query failed in sidebar fallback: %s", exc, exc_info=True
163
+ )
128
164
 
129
165
  def _item_visible(item: Any) -> bool:
130
166
  return (
@@ -187,9 +223,10 @@ class AdminTemplate:
187
223
  "current_user": user_for_template,
188
224
  "dashboard_visible": dashboard_visible,
189
225
  "settings_visible": settings_visible,
226
+ "sidebar_bottom_links": self.sidebar_bottom_links,
190
227
  }
191
228
 
192
- def apply_sidebar_context(self, request: Any, user: Any, context: dict) -> dict:
229
+ async def apply_sidebar_context(self, request: Any, user: Any, context: dict) -> dict:
193
230
  """Inject nav_groups + permissions_map into a template context dict."""
194
- context.update(self.build_sidebar_context(request, user=user))
231
+ context.update(await self.build_sidebar_context(request, user=user))
195
232
  return context
@@ -35,6 +35,7 @@ if TYPE_CHECKING:
35
35
  from sqlalchemy.engine import Engine
36
36
 
37
37
  from fastapi_admin_kit.auth.backend import AuthBackend
38
+ from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyBackend
38
39
  from fastapi_admin_kit.nav import NavGroupConfig, SidebarBuilder
39
40
  from fastapi_admin_kit.storage.base import StorageBackend
40
41
  from fastapi_admin_kit.views import ModelAdmin
@@ -124,6 +125,7 @@ class Admin:
124
125
  database: AdminDatabase | None = None,
125
126
  router: AdminRouter | None = None,
126
127
  template: AdminTemplate | None = None,
128
+ backend: SqlAlchemyBackend | None = None,
127
129
  # Legacy kwargs for backward compatibility
128
130
  base: type | None = None,
129
131
  title: str = "FastAPI Admin Kit",
@@ -178,6 +180,7 @@ class Admin:
178
180
  mobile_sidebar: str = "overlay",
179
181
  dashboard_permission: str | None = None,
180
182
  settings_permission: str | None = None,
183
+ sidebar_bottom_links: list[dict[str, str]] | None = None,
181
184
  ):
182
185
  self.registry = AdminRegistry()
183
186
  self._app: FastAPI | None = app
@@ -259,6 +262,7 @@ class Admin:
259
262
  require_tags=require_tags,
260
263
  dashboard_permission=dashboard_permission,
261
264
  settings_permission=settings_permission,
265
+ sidebar_bottom_links=sidebar_bottom_links,
262
266
  ),
263
267
  )
264
268
 
@@ -281,6 +285,7 @@ class Admin:
281
285
  dark_mode_default=config.ui.dark_mode_default,
282
286
  dashboard_permission=config.nav.dashboard_permission,
283
287
  settings_permission=config.nav.settings_permission,
288
+ sidebar_bottom_links=config.nav.sidebar_bottom_links,
284
289
  )
285
290
 
286
291
  self.config = config
@@ -288,6 +293,16 @@ class Admin:
288
293
  self.router = router
289
294
  self.template = template
290
295
 
296
+ # Backend: defaults to composed SqlAlchemyBackend
297
+ if backend is None:
298
+ from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyBackend
299
+
300
+ backend = SqlAlchemyBackend.from_admin_database(database)
301
+ self.backend = backend
302
+
303
+ # Inject backend's introspection adapter into the registry's ModelInspector
304
+ self.registry.inspector._adapter = self.backend.introspection
305
+
291
306
  # RBAC
292
307
  self.seed_roles = seed_roles if seed_roles is not None else DEFAULT_SEED_ROLES
293
308
  self.seed_roles_overwrite = seed_roles_overwrite
@@ -535,8 +550,6 @@ class Admin:
535
550
  registered.admin.skip_auto_routes = True
536
551
 
537
552
  # 8.3 Attach audit event listeners (after registry is populated)
538
- from fastapi_admin_kit.audit.listener import attach_audit_listener
539
-
540
553
  engine = self.database.engine
541
554
  if engine is not None:
542
555
  from sqlalchemy.ext.asyncio import AsyncEngine
@@ -545,7 +558,12 @@ class Admin:
545
558
  from fastapi_admin_kit.db import create_session_factory
546
559
 
547
560
  session_factory = create_session_factory(engine)
548
- attach_audit_listener(session_factory, self.registry)
561
+ from fastapi_admin_kit.backends.sqlalchemy import (
562
+ SqlAlchemyAuditBackend,
563
+ )
564
+
565
+ audit_backend = SqlAlchemyAuditBackend()
566
+ audit_backend.attach_listeners(session_factory, self.registry)
549
567
 
550
568
  # 9. Validate require_tags
551
569
  if self.config.nav.require_tags:
@@ -703,6 +721,7 @@ class Admin:
703
721
  admin_instance=self,
704
722
  secret_key=self.router.secret_key,
705
723
  session_samesite=self.config.auth.session_samesite,
724
+ backend=self.backend,
706
725
  )
707
726
 
708
727
  # Store typed state as single attribute
@@ -721,6 +740,16 @@ class Admin:
721
740
  app.state.admin_jinja_env = state.jinja_env
722
741
  # Unified signing-key source for sessions, CSRF, and JWT (see AdminState).
723
742
  app.state.admin_secret_key = state.secret_key
743
+ # Multi-ORM backend: store composed backend and derive individual adapters
744
+ from fastapi_admin_kit.backends.sqlalchemy import (
745
+ SqlAlchemySessionAdapter,
746
+ )
747
+
748
+ app.state.admin_backend = self.backend
749
+ app.state.admin_session_backend_class = SqlAlchemySessionAdapter
750
+ app.state.admin_query_adapter = self.backend.query
751
+ app.state.admin_introspection_adapter = self.backend.introspection
752
+ app.state.admin_audit_backend = self.backend.audit
724
753
 
725
754
  # Wire the password hasher to the User model
726
755
  from fastapi_admin_kit.auth.models import User
@@ -826,6 +855,7 @@ class Admin:
826
855
  "bars-": "menu",
827
856
  "bars-3": "menu",
828
857
  "arrow-down-tray": "download",
858
+ "arrow-up-tray": "upload",
829
859
  "arrow-path": "refresh",
830
860
  "paper-airplane": "send",
831
861
  "exclamation-triangle": "warning",
@@ -1018,21 +1048,21 @@ class Admin:
1018
1048
  admin_path=self.router.admin_path,
1019
1049
  )
1020
1050
 
1021
- def build_sidebar_context(
1051
+ async def build_sidebar_context(
1022
1052
  self,
1023
1053
  request: Any,
1024
1054
  user: Any = None,
1025
1055
  permissions_map: dict | None = None,
1026
1056
  ) -> dict:
1027
1057
  """Build per-request sidebar context (RBAC filter + permissions map)."""
1028
- return self.template.build_sidebar_context(
1058
+ return await self.template.build_sidebar_context(
1029
1059
  request, user=user, permissions_map=permissions_map
1030
1060
  )
1031
1061
 
1032
- def sidebar_template_kwargs(self, request: Any) -> dict[str, Any]:
1062
+ async def sidebar_template_kwargs(self, request: Any) -> dict[str, Any]:
1033
1063
  """Thin wrapper — returns sidebar kwargs for TemplateResponse contexts."""
1034
- return self.template.sidebar_template_kwargs(request)
1064
+ return await self.template.sidebar_template_kwargs(request)
1035
1065
 
1036
- def apply_sidebar_context(self, request: Any, user: Any, context: dict) -> dict:
1066
+ async def apply_sidebar_context(self, request: Any, user: Any, context: dict) -> dict:
1037
1067
  """Inject nav_groups + permissions_map into a template context dict."""
1038
- return self.template.apply_sidebar_context(request, user, context)
1068
+ return await self.template.apply_sidebar_context(request, user, context)
@@ -12,6 +12,7 @@ if TYPE_CHECKING:
12
12
  from fastapi_admin_kit.admin.core import Admin
13
13
  from fastapi_admin_kit.auth.backend import AuthBackend
14
14
  from fastapi_admin_kit.auth.session import SignedCookieSessionBackend
15
+ from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyBackend
15
16
  from fastapi_admin_kit.registry import AdminRegistry
16
17
  from fastapi_admin_kit.storage.base import StorageBackend
17
18
 
@@ -36,6 +37,8 @@ class AdminState:
36
37
  # Unified signing-key source — used by signed-cookie sessions, CSRF, and JWT.
37
38
  secret_key: str = ""
38
39
  session_samesite: str = "strict"
40
+ # Multi-ORM backend — composes introspection, query, audit, database adapters.
41
+ backend: SqlAlchemyBackend | None = None
39
42
 
40
43
  @classmethod
41
44
  def from_request(cls, request: Any) -> AdminState:
@@ -73,4 +76,5 @@ class AdminState:
73
76
  jinja_env=getattr(app_state, "admin_jinja_env", None),
74
77
  admin_instance=getattr(app_state, "admin", None),
75
78
  secret_key=getattr(app_state, "admin_secret_key", ""),
79
+ backend=getattr(app_state, "admin_backend", None),
76
80
  )
@@ -3,7 +3,10 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from contextvars import ContextVar
6
- from typing import Any
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ if TYPE_CHECKING:
9
+ from fastapi_admin_kit.audit.events import AuditEvent
7
10
 
8
11
  # Context variable to store audit context (user, IP, user_agent, etc.)
9
12
  _current_audit_context: ContextVar[dict] = ContextVar("_current_audit_context", default={})
@@ -60,3 +63,32 @@ class AuditContext:
60
63
  def clear_context(self) -> None:
61
64
  """Clear the audit context."""
62
65
  clear_audit_context()
66
+
67
+
68
+ async def log_audit_event(request: Any, event: AuditEvent) -> None:
69
+ """Persist an AuditEvent directly to the database.
70
+
71
+ Used for EXPORT/IMPORT events that don't go through the SQLAlchemy
72
+ before_flush listener.
73
+ """
74
+ from fastapi_admin_kit.audit.models import AuditLog
75
+ from fastapi_admin_kit.db import get_db_session
76
+
77
+ session = get_db_session(request)
78
+ ctx = get_audit_context()
79
+
80
+ entry = AuditLog(
81
+ action=event.event_type,
82
+ model_name=event.model_name,
83
+ table_name=event.table_name,
84
+ object_id=event.object_id,
85
+ object_repr=event.object_repr,
86
+ changes=event.changes,
87
+ full_snapshot=event.full_snapshot,
88
+ user_id=event.user_id or ctx.get("user_id"),
89
+ user_email=event.user_email or ctx.get("user_email"),
90
+ ip_address=event.ip_address or ctx.get("ip_address"),
91
+ user_agent=event.user_agent or ctx.get("user_agent"),
92
+ )
93
+ session.add(entry)
94
+ await session.flush()
@@ -1,4 +1,8 @@
1
- """Audit diff utilities — snapshot and diff computation for SQLAlchemy models."""
1
+ """Audit diff utilities — snapshot and diff computation for SQLAlchemy models.
2
+
3
+ ``serialize_value()`` is ORM-agnostic and stays here.
4
+ ``snapshot()`` and ``compute_diff()`` delegate to the active AuditBackend.
5
+ """
2
6
 
3
7
  from __future__ import annotations
4
8
 
@@ -8,8 +12,6 @@ import enum
8
12
  from typing import Any
9
13
  from uuid import UUID
10
14
 
11
- from sqlalchemy.inspection import inspect as sqlalchemy_inspect
12
-
13
15
 
14
16
  def serialize_value(val: Any) -> Any:
15
17
  """Convert a value to a JSON-serializable form.
@@ -33,12 +35,9 @@ def serialize_value(val: Any) -> Any:
33
35
  if isinstance(val, UUID):
34
36
  return str(val)
35
37
  if isinstance(val, bytes):
36
- # For simplicity, we'll return the hex representation.
37
- # Alternatively, we could use base64, but hex is simpler.
38
38
  return val.hex()
39
39
  if isinstance(val, enum.Enum):
40
40
  return val.value
41
- # For other types, we assume they are JSON serializable (or let JSON encoder handle it)
42
41
  return val
43
42
 
44
43
 
@@ -46,18 +45,12 @@ def snapshot(obj: Any) -> dict[str, Any]:
46
45
  """Snapshot all mapped columns of a SQLAlchemy model instance.
47
46
 
48
47
  Returns a dict mapping column name to serialized value.
48
+ Delegates to the active AuditBackend.
49
49
  """
50
- if not hasattr(obj, "__table__"):
51
- raise ValueError("Object is not a SQLAlchemy model instance")
50
+ from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
52
51
 
53
- mapper = sqlalchemy_inspect(obj.__class__)
54
- data = {}
55
- for column in mapper.columns:
56
- # Skip foreign key columns that are represented by relationships?
57
- # We'll include all columns for simplicity.
58
- value = getattr(obj, column.key)
59
- data[column.key] = serialize_value(value)
60
- return data
52
+ backend = SqlAlchemyAuditBackend()
53
+ return backend.snapshot(obj)
61
54
 
62
55
 
63
56
  def compute_diff(before: dict[str, Any], after: dict[str, Any]) -> dict[str, Any]:
@@ -66,12 +59,9 @@ def compute_diff(before: dict[str, Any], after: dict[str, Any]) -> dict[str, Any
66
59
  Returns a dict of changed fields, each containing:
67
60
  {"old": <value>, "new": <value>}
68
61
  Only fields that have changed are included.
62
+ Delegates to the active AuditBackend.
69
63
  """
70
- diff = {}
71
- all_keys = set(before.keys()) | set(after.keys())
72
- for key in all_keys:
73
- old_val = before.get(key)
74
- new_val = after.get(key)
75
- if old_val != new_val:
76
- diff[key] = {"old": old_val, "new": new_val}
77
- return diff
64
+ from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
65
+
66
+ backend = SqlAlchemyAuditBackend()
67
+ return backend.compute_diff(before, after)
@@ -9,13 +9,13 @@ from typing import Any
9
9
 
10
10
  @dataclass
11
11
  class AuditEvent:
12
- """Represents a single audit event (CREATE, UPDATE, or DELETE).
12
+ """Represents a single audit event (CREATE, UPDATE, DELETE, EXPORT, or IMPORT).
13
13
 
14
14
  This is a pure data structure with no side effects, making it
15
15
  easy to test and serialize.
16
16
  """
17
17
 
18
- event_type: str # "CREATE" | "UPDATE" | "DELETE"
18
+ event_type: str # "CREATE" | "UPDATE" | "DELETE" | "EXPORT" | "IMPORT"
19
19
  model_name: str
20
20
  table_name: str
21
21
  object_id: str
@@ -55,31 +55,29 @@ def _snapshot_from_committed(obj: Any) -> dict[str, Any]:
55
55
 
56
56
 
57
57
  def _snapshot_current(obj: Any) -> dict[str, Any]:
58
- """Snapshot all mapped columns of a SQLAlchemy model instance."""
59
- if not hasattr(obj, "__table__"):
60
- return {}
61
- from sqlalchemy.inspection import inspect as sa_inspect
58
+ """Snapshot all mapped columns of a SQLAlchemy model instance.
62
59
 
63
- mapper = sa_inspect(obj.__class__)
64
- data: dict[str, Any] = {}
65
- for column in mapper.columns:
66
- data[column.key] = serialize_value(getattr(obj, column.key))
67
- return data
60
+ Delegates to the SqlAlchemyAuditBackend. Returns {} for non-model objects.
61
+ """
62
+ from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
63
+
64
+ backend = SqlAlchemyAuditBackend()
65
+ try:
66
+ return backend.snapshot(obj)
67
+ except ValueError:
68
+ return {}
68
69
 
69
70
 
70
71
  def _compute_diff(before: dict[str, Any], after: dict[str, Any]) -> dict[str, Any]:
71
72
  """Compute changed fields between two snapshots.
72
73
 
73
74
  Returns ``{"field": {"old": ..., "new": ...}}`` for each difference.
75
+ Delegates to the SqlAlchemyAuditBackend.
74
76
  """
75
- diff: dict[str, Any] = {}
76
- all_keys = set(before.keys()) | set(after.keys())
77
- for key in all_keys:
78
- old_val = before.get(key)
79
- new_val = after.get(key)
80
- if old_val != new_val:
81
- diff[key] = {"old": old_val, "new": new_val}
82
- return diff
77
+ from fastapi_admin_kit.backends.sqlalchemy import SqlAlchemyAuditBackend
78
+
79
+ backend = SqlAlchemyAuditBackend()
80
+ return backend.compute_diff(before, after)
83
81
 
84
82
 
85
83
  def _build_audit_row(
@@ -26,3 +26,11 @@ class AuditLogger(ABC):
26
26
  @abstractmethod
27
27
  def log_delete(self, event: AuditEvent) -> None:
28
28
  """Persist a DELETE audit event."""
29
+
30
+ @abstractmethod
31
+ def log_export(self, event: AuditEvent) -> None:
32
+ """Persist an EXPORT audit event."""
33
+
34
+ @abstractmethod
35
+ def log_import(self, event: AuditEvent) -> None:
36
+ """Persist an IMPORT audit event."""
@@ -32,6 +32,12 @@ class SqlAlchemyAuditLogger(AuditLogger):
32
32
  def log_delete(self, event: AuditEvent) -> None:
33
33
  self._buffer(event)
34
34
 
35
+ def log_export(self, event: AuditEvent) -> None:
36
+ self._buffer(event)
37
+
38
+ def log_import(self, event: AuditEvent) -> None:
39
+ self._buffer(event)
40
+
35
41
  def _buffer(self, event: AuditEvent) -> None:
36
42
  entry = AuditLog(
37
43
  user_id=event.user_id,
@@ -17,14 +17,28 @@ from fastapi_admin_kit.auth.models import (
17
17
  User,
18
18
  UserPermission,
19
19
  )
20
+ from fastapi_admin_kit.auth.password import (
21
+ PasswordManager,
22
+ password_manager,
23
+ validate_password_strength,
24
+ )
25
+ from fastapi_admin_kit.auth.protocol import (
26
+ AdminPermissionProtocol,
27
+ AdminRoleProtocol,
28
+ AdminUserProtocol,
29
+ )
20
30
  from fastapi_admin_kit.auth.session import (
21
31
  SessionBackend,
22
32
  SignedCookieSessionBackend,
23
33
  )
24
34
 
25
35
  __all__ = [
36
+ "AdminPermissionProtocol",
37
+ "AdminRoleProtocol",
38
+ "AdminUserProtocol",
26
39
  "AuthModelMixin",
27
40
  "BcryptHasher",
41
+ "PasswordManager",
28
42
  "Permission",
29
43
  "PasswordHasher",
30
44
  "Role",
@@ -36,6 +50,8 @@ __all__ = [
36
50
  "SignedCookieSessionBackend",
37
51
  "auth_redirect_handler",
38
52
  "forbidden_handler",
53
+ "password_manager",
39
54
  "require_csrf_token",
40
55
  "set_csrf_cookie",
56
+ "validate_password_strength",
41
57
  ]