litedmin 0.4.0__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 (222) hide show
  1. litedmin-0.4.0/CHANGELOG.md +76 -0
  2. litedmin-0.4.0/LICENSE +21 -0
  3. litedmin-0.4.0/MANIFEST.in +10 -0
  4. litedmin-0.4.0/PKG-INFO +302 -0
  5. litedmin-0.4.0/README.md +242 -0
  6. litedmin-0.4.0/litedmin/__init__.py +136 -0
  7. litedmin-0.4.0/litedmin/__main__.py +6 -0
  8. litedmin-0.4.0/litedmin/backend/__init__.py +1 -0
  9. litedmin-0.4.0/litedmin/backend/admin/__init__.py +17 -0
  10. litedmin-0.4.0/litedmin/backend/admin/base.py +902 -0
  11. litedmin-0.4.0/litedmin/backend/admin/changelist.py +211 -0
  12. litedmin-0.4.0/litedmin/backend/admin/checks.py +119 -0
  13. litedmin-0.4.0/litedmin/backend/admin/display_columns.py +221 -0
  14. litedmin-0.4.0/litedmin/backend/admin/export.py +57 -0
  15. litedmin-0.4.0/litedmin/backend/admin/formsets.py +299 -0
  16. litedmin-0.4.0/litedmin/backend/admin/list_projection.py +147 -0
  17. litedmin-0.4.0/litedmin/backend/admin/mixins.py +75 -0
  18. litedmin-0.4.0/litedmin/backend/admin/pagination.py +149 -0
  19. litedmin-0.4.0/litedmin/backend/admin/permissions.py +103 -0
  20. litedmin-0.4.0/litedmin/backend/admin/scope.py +8 -0
  21. litedmin-0.4.0/litedmin/backend/api/__init__.py +26 -0
  22. litedmin-0.4.0/litedmin/backend/api/autocomplete.py +214 -0
  23. litedmin-0.4.0/litedmin/backend/api/core.py +1017 -0
  24. litedmin-0.4.0/litedmin/backend/api/endpoints.py +352 -0
  25. litedmin-0.4.0/litedmin/backend/api/history/__init__.py +11 -0
  26. litedmin-0.4.0/litedmin/backend/api/history/repository.py +273 -0
  27. litedmin-0.4.0/litedmin/backend/api/history/service.py +105 -0
  28. litedmin-0.4.0/litedmin/backend/api/responses.py +67 -0
  29. litedmin-0.4.0/litedmin/backend/auth/__init__.py +69 -0
  30. litedmin-0.4.0/litedmin/backend/auth/admin.py +298 -0
  31. litedmin-0.4.0/litedmin/backend/auth/auth.py +172 -0
  32. litedmin-0.4.0/litedmin/backend/auth/backends.py +191 -0
  33. litedmin-0.4.0/litedmin/backend/auth/cookies.py +37 -0
  34. litedmin-0.4.0/litedmin/backend/auth/csrf.py +99 -0
  35. litedmin-0.4.0/litedmin/backend/auth/dependencies.py +205 -0
  36. litedmin-0.4.0/litedmin/backend/auth/gateway_web.py +266 -0
  37. litedmin-0.4.0/litedmin/backend/auth/hashers.py +186 -0
  38. litedmin-0.4.0/litedmin/backend/auth/identity.py +118 -0
  39. litedmin-0.4.0/litedmin/backend/auth/models/__init__.py +75 -0
  40. litedmin-0.4.0/litedmin/backend/auth/models/registry.py +162 -0
  41. litedmin-0.4.0/litedmin/backend/auth/models/sqlalchemy/__init__.py +21 -0
  42. litedmin-0.4.0/litedmin/backend/auth/models/sqlalchemy/base.py +5 -0
  43. litedmin-0.4.0/litedmin/backend/auth/models/sqlalchemy/builder.py +148 -0
  44. litedmin-0.4.0/litedmin/backend/auth/models/sqlalchemy/models.py +193 -0
  45. litedmin-0.4.0/litedmin/backend/auth/models/tortoise/__init__.py +19 -0
  46. litedmin-0.4.0/litedmin/backend/auth/models/tortoise/builder.py +144 -0
  47. litedmin-0.4.0/litedmin/backend/auth/models/tortoise/core.py +66 -0
  48. litedmin-0.4.0/litedmin/backend/auth/models/tortoise/models.py +125 -0
  49. litedmin-0.4.0/litedmin/backend/auth/permission_service.py +424 -0
  50. litedmin-0.4.0/litedmin/backend/auth/resolver.py +34 -0
  51. litedmin-0.4.0/litedmin/backend/auth/session.py +253 -0
  52. litedmin-0.4.0/litedmin/backend/auth/settings.py +17 -0
  53. litedmin-0.4.0/litedmin/backend/auth/user_model.py +101 -0
  54. litedmin-0.4.0/litedmin/backend/auth/utils.py +197 -0
  55. litedmin-0.4.0/litedmin/backend/core/__init__.py +1 -0
  56. litedmin-0.4.0/litedmin/backend/core/action.py +91 -0
  57. litedmin-0.4.0/litedmin/backend/core/config.py +110 -0
  58. litedmin-0.4.0/litedmin/backend/core/decorators.py +62 -0
  59. litedmin-0.4.0/litedmin/backend/core/display.py +90 -0
  60. litedmin-0.4.0/litedmin/backend/core/enums.py +80 -0
  61. litedmin-0.4.0/litedmin/backend/core/exceptions.py +43 -0
  62. litedmin-0.4.0/litedmin/backend/core/fields.py +224 -0
  63. litedmin-0.4.0/litedmin/backend/core/introspection.py +118 -0
  64. litedmin-0.4.0/litedmin/backend/core/orm_settings.py +42 -0
  65. litedmin-0.4.0/litedmin/backend/core/request.py +55 -0
  66. litedmin-0.4.0/litedmin/backend/core/schema_generator.py +895 -0
  67. litedmin-0.4.0/litedmin/backend/core/site.py +370 -0
  68. litedmin-0.4.0/litedmin/backend/core/utils.py +162 -0
  69. litedmin-0.4.0/litedmin/backend/core/widgets.py +100 -0
  70. litedmin-0.4.0/litedmin/backend/dto/__init__.py +137 -0
  71. litedmin-0.4.0/litedmin/backend/dto/auth.py +82 -0
  72. litedmin-0.4.0/litedmin/backend/dto/base.py +39 -0
  73. litedmin-0.4.0/litedmin/backend/dto/config.py +80 -0
  74. litedmin-0.4.0/litedmin/backend/dto/permissions.py +19 -0
  75. litedmin-0.4.0/litedmin/backend/dto/records.py +214 -0
  76. litedmin-0.4.0/litedmin/backend/dto/requests.py +519 -0
  77. litedmin-0.4.0/litedmin/backend/dto/responses.py +367 -0
  78. litedmin-0.4.0/litedmin/backend/integration/__init__.py +56 -0
  79. litedmin-0.4.0/litedmin/backend/integration/admin_lifecycle.py +136 -0
  80. litedmin-0.4.0/litedmin/backend/integration/asgi/__init__.py +5 -0
  81. litedmin-0.4.0/litedmin/backend/integration/asgi/admin.py +307 -0
  82. litedmin-0.4.0/litedmin/backend/integration/asgi/request.py +43 -0
  83. litedmin-0.4.0/litedmin/backend/integration/asgi/responses.py +43 -0
  84. litedmin-0.4.0/litedmin/backend/integration/asgi/routes.py +475 -0
  85. litedmin-0.4.0/litedmin/backend/integration/asgi/spa.py +71 -0
  86. litedmin-0.4.0/litedmin/backend/integration/asgi/trailing_slash.py +49 -0
  87. litedmin-0.4.0/litedmin/backend/integration/csrf_middleware.py +101 -0
  88. litedmin-0.4.0/litedmin/backend/integration/di/__init__.py +21 -0
  89. litedmin-0.4.0/litedmin/backend/integration/di/container.py +49 -0
  90. litedmin-0.4.0/litedmin/backend/integration/di/fastapi_integration.py +45 -0
  91. litedmin-0.4.0/litedmin/backend/integration/di/providers.py +100 -0
  92. litedmin-0.4.0/litedmin/backend/integration/fastapi/__init__.py +7 -0
  93. litedmin-0.4.0/litedmin/backend/integration/fastapi/admin.py +35 -0
  94. litedmin-0.4.0/litedmin/backend/integration/frontend_paths.py +35 -0
  95. litedmin-0.4.0/litedmin/backend/integration/handlers.py +168 -0
  96. litedmin-0.4.0/litedmin/backend/integration/litestar/__init__.py +7 -0
  97. litedmin-0.4.0/litedmin/backend/integration/litestar/admin.py +40 -0
  98. litedmin-0.4.0/litedmin/backend/integration/msgspec_support.py +63 -0
  99. litedmin-0.4.0/litedmin/backend/integration/orm_registry.py +99 -0
  100. litedmin-0.4.0/litedmin/backend/integration/static.py +50 -0
  101. litedmin-0.4.0/litedmin/backend/orm/__init__.py +35 -0
  102. litedmin-0.4.0/litedmin/backend/orm/base.py +130 -0
  103. litedmin-0.4.0/litedmin/backend/orm/batch.py +42 -0
  104. litedmin-0.4.0/litedmin/backend/orm/date_filters.py +68 -0
  105. litedmin-0.4.0/litedmin/backend/orm/pk.py +80 -0
  106. litedmin-0.4.0/litedmin/backend/orm/protocols.py +144 -0
  107. litedmin-0.4.0/litedmin/backend/orm/registry.py +156 -0
  108. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/__init__.py +50 -0
  109. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/adapter.py +8 -0
  110. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/admin.py +40 -0
  111. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/backend.py +56 -0
  112. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/init.py +251 -0
  113. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/introspector.py +254 -0
  114. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/repository.py +740 -0
  115. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/schema_generator.py +15 -0
  116. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/session.py +94 -0
  117. litedmin-0.4.0/litedmin/backend/orm/sqlalchemy/types.py +53 -0
  118. litedmin-0.4.0/litedmin/backend/orm/tortoise/__init__.py +44 -0
  119. litedmin-0.4.0/litedmin/backend/orm/tortoise/adapter.py +9 -0
  120. litedmin-0.4.0/litedmin/backend/orm/tortoise/admin.py +42 -0
  121. litedmin-0.4.0/litedmin/backend/orm/tortoise/backend.py +56 -0
  122. litedmin-0.4.0/litedmin/backend/orm/tortoise/init.py +103 -0
  123. litedmin-0.4.0/litedmin/backend/orm/tortoise/introspector.py +176 -0
  124. litedmin-0.4.0/litedmin/backend/orm/tortoise/repository.py +284 -0
  125. litedmin-0.4.0/litedmin/backend/orm/tortoise/schema_generator.py +15 -0
  126. litedmin-0.4.0/litedmin/backend/orm/tortoise/types.py +81 -0
  127. litedmin-0.4.0/litedmin/backend/storage/__init__.py +5 -0
  128. litedmin-0.4.0/litedmin/backend/storage/protocol.py +33 -0
  129. litedmin-0.4.0/litedmin/cli.py +195 -0
  130. litedmin-0.4.0/litedmin/py.typed +0 -0
  131. litedmin-0.4.0/litedmin/static/asset-manifest.json +85 -0
  132. litedmin-0.4.0/litedmin/static/favicon.ico +0 -0
  133. litedmin-0.4.0/litedmin/static/index.html +1 -0
  134. litedmin-0.4.0/litedmin/static/logo192.png +0 -0
  135. litedmin-0.4.0/litedmin/static/logo512.png +0 -0
  136. litedmin-0.4.0/litedmin/static/manifest.json +25 -0
  137. litedmin-0.4.0/litedmin/static/robots.txt +3 -0
  138. litedmin-0.4.0/litedmin/static/static/css/main.c336e45a.css +2 -0
  139. litedmin-0.4.0/litedmin/static/static/css/main.c336e45a.css.map +1 -0
  140. litedmin-0.4.0/litedmin/static/static/js/453.7b4f4f73.chunk.js +2 -0
  141. litedmin-0.4.0/litedmin/static/static/js/453.7b4f4f73.chunk.js.map +1 -0
  142. litedmin-0.4.0/litedmin/static/static/js/main.06b3b9a6.js +156 -0
  143. litedmin-0.4.0/litedmin/static/static/js/main.06b3b9a6.js.LICENSE.txt +91 -0
  144. litedmin-0.4.0/litedmin/static/static/js/main.06b3b9a6.js.map +1 -0
  145. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-300-normal.ead2b63c30510e752332.woff +0 -0
  146. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-300-normal.f9e5f604a45c00a532f2.woff2 +0 -0
  147. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-400-normal.cb04b2ee2f7d192478b8.woff2 +0 -0
  148. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-400-normal.e2841352dd9512ff455b.woff +0 -0
  149. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-500-normal.b9dcbca183ac93f02061.woff2 +0 -0
  150. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-500-normal.e824ac3c6c579cd0fe85.woff +0 -0
  151. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-600-normal.c556b5d042ebf128e47c.woff +0 -0
  152. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-600-normal.ce60970447cde0dd1582.woff2 +0 -0
  153. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-700-normal.36e9cf9ecc07ac6bce28.woff2 +0 -0
  154. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-700-normal.534fd15b796bf021d0be.woff +0 -0
  155. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-300-normal.04c5d2c480c226920a9b.woff2 +0 -0
  156. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-300-normal.eb97c8e4e9062ed5dfd2.woff +0 -0
  157. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-400-normal.e89451624c6caf35687f.woff +0 -0
  158. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-400-normal.fd1478dcc74de00ee4c0.woff2 +0 -0
  159. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-500-normal.023db878f10be2203b60.woff2 +0 -0
  160. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-500-normal.9160d5ba9e6099f95032.woff +0 -0
  161. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-600-normal.25b7c495f686777a1c08.woff2 +0 -0
  162. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-600-normal.aa900fea9aed1ba3c903.woff +0 -0
  163. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-700-normal.3cf42e401103d3211022.woff +0 -0
  164. litedmin-0.4.0/litedmin/static/static/media/inter-cyrillic-ext-700-normal.5631f054acb5a33a716a.woff2 +0 -0
  165. litedmin-0.4.0/litedmin/static/static/media/inter-greek-300-normal.3703eb6abf02d92c4c6d.woff2 +0 -0
  166. litedmin-0.4.0/litedmin/static/static/media/inter-greek-300-normal.e35098a79e23df5c2282.woff +0 -0
  167. litedmin-0.4.0/litedmin/static/static/media/inter-greek-400-normal.a42da273bb968bf2058d.woff +0 -0
  168. litedmin-0.4.0/litedmin/static/static/media/inter-greek-400-normal.a8de720a6f13c66c8a4e.woff2 +0 -0
  169. litedmin-0.4.0/litedmin/static/static/media/inter-greek-500-normal.0d91a975f010051e7150.woff2 +0 -0
  170. litedmin-0.4.0/litedmin/static/static/media/inter-greek-500-normal.cbc71955e9956dcd2882.woff +0 -0
  171. litedmin-0.4.0/litedmin/static/static/media/inter-greek-600-normal.5c46a357ea91911fc65d.woff2 +0 -0
  172. litedmin-0.4.0/litedmin/static/static/media/inter-greek-600-normal.ba3b8b69a69b0e83d453.woff +0 -0
  173. litedmin-0.4.0/litedmin/static/static/media/inter-greek-700-normal.f47ebf852d0b0e251a80.woff +0 -0
  174. litedmin-0.4.0/litedmin/static/static/media/inter-greek-700-normal.f5fbdfc39c2431160896.woff2 +0 -0
  175. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-300-normal.6479663fa09debba7d48.woff +0 -0
  176. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-300-normal.c0dba9f477d8b8e0198f.woff2 +0 -0
  177. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-400-normal.b9e1e89444a313a63b91.woff +0 -0
  178. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-400-normal.f2fa0d9ef98f59d4a75b.woff2 +0 -0
  179. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-500-normal.7f67cfd4832dde0e0494.woff +0 -0
  180. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-500-normal.b187635a3d8a0b90faa0.woff2 +0 -0
  181. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-600-normal.7e7594e0432b84536054.woff +0 -0
  182. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-600-normal.c8a7cddec49294a9e8ba.woff2 +0 -0
  183. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-700-normal.4c7cc2277f187ed0ef37.woff +0 -0
  184. litedmin-0.4.0/litedmin/static/static/media/inter-greek-ext-700-normal.4cdf2b4e817dade3d4aa.woff2 +0 -0
  185. litedmin-0.4.0/litedmin/static/static/media/inter-latin-300-normal.6e6082ec285b38897e2f.woff2 +0 -0
  186. litedmin-0.4.0/litedmin/static/static/media/inter-latin-300-normal.b7ec8f2cad1cc72e39fa.woff +0 -0
  187. litedmin-0.4.0/litedmin/static/static/media/inter-latin-400-normal.9698cc7dbf147ed363b6.woff2 +0 -0
  188. litedmin-0.4.0/litedmin/static/static/media/inter-latin-400-normal.b0c8fe9db56dd415a25a.woff +0 -0
  189. litedmin-0.4.0/litedmin/static/static/media/inter-latin-500-normal.5b7156f802c053798d95.woff2 +0 -0
  190. litedmin-0.4.0/litedmin/static/static/media/inter-latin-500-normal.f46567788e464ac34d10.woff +0 -0
  191. litedmin-0.4.0/litedmin/static/static/media/inter-latin-600-normal.28c8f6060760ec1a5d25.woff +0 -0
  192. litedmin-0.4.0/litedmin/static/static/media/inter-latin-600-normal.9d111139a583baef3053.woff2 +0 -0
  193. litedmin-0.4.0/litedmin/static/static/media/inter-latin-700-normal.d42de9b25ac31d2a2a8b.woff2 +0 -0
  194. litedmin-0.4.0/litedmin/static/static/media/inter-latin-700-normal.fc47ca451598e4b97249.woff +0 -0
  195. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-300-normal.1230c922d601129d8b4c.woff +0 -0
  196. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-300-normal.f95a500034ca27bc4af3.woff2 +0 -0
  197. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-400-normal.1c20f7dc20d731a18fcd.woff +0 -0
  198. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-400-normal.3d10c85fbd16969509a2.woff2 +0 -0
  199. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-500-normal.2b8be079a0e965b2f035.woff +0 -0
  200. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-500-normal.667f22c9b36deaea0f54.woff2 +0 -0
  201. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-600-normal.0acb54c755dec53de476.woff +0 -0
  202. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-600-normal.ae5f9691ebe48c908e8c.woff2 +0 -0
  203. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-700-normal.149d81a49bd1ff0e6711.woff +0 -0
  204. litedmin-0.4.0/litedmin/static/static/media/inter-latin-ext-700-normal.96e3bd6bb1f980376cf1.woff2 +0 -0
  205. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-300-normal.2a4055b17286ecdd503d.woff2 +0 -0
  206. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-300-normal.39c118d908e573021d87.woff +0 -0
  207. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-400-normal.44c9df135eec470b0ab6.woff2 +0 -0
  208. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-400-normal.96f8adc74b63e1a3655f.woff +0 -0
  209. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-500-normal.11aeae6e31a0d8c24c83.woff +0 -0
  210. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-500-normal.63d02ea896a7406849b6.woff2 +0 -0
  211. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-600-normal.6984cf5e8583fac83be3.woff2 +0 -0
  212. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-600-normal.a10931d3a643f48663b9.woff +0 -0
  213. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-700-normal.5b5cd0c77cad23651d04.woff +0 -0
  214. litedmin-0.4.0/litedmin/static/static/media/inter-vietnamese-700-normal.5d9bfcacc44f345662ff.woff2 +0 -0
  215. litedmin-0.4.0/litedmin.egg-info/PKG-INFO +302 -0
  216. litedmin-0.4.0/litedmin.egg-info/SOURCES.txt +220 -0
  217. litedmin-0.4.0/litedmin.egg-info/dependency_links.txt +1 -0
  218. litedmin-0.4.0/litedmin.egg-info/entry_points.txt +2 -0
  219. litedmin-0.4.0/litedmin.egg-info/requires.txt +36 -0
  220. litedmin-0.4.0/litedmin.egg-info/top_level.txt +1 -0
  221. litedmin-0.4.0/pyproject.toml +127 -0
  222. litedmin-0.4.0/setup.cfg +4 -0
@@ -0,0 +1,76 @@
1
+ # Changelog
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.4.0 — 2026-10-30
6
+
7
+ Public beta on [PyPI](https://pypi.org/project/litedmin/).
8
+
9
+ ### Breaking changes
10
+ - List actions must be declared with **`@action`** — undecorated callables in `ModelAdmin.actions` raise `ImproperlyConfigured`
11
+
12
+ ### Added
13
+ - `@action` decorator (Django `@admin.action` style): `description`, `row`, `bulk`, `toolbar`, `requires_confirmation`, `allow_empty`
14
+ - Row-level action buttons when `row=True`; page toolbar buttons when `toolbar=True`
15
+ - `BulkActionDTO.row` / `bulk` / `toolbar` in list schema
16
+ - Action return value (`str` / `{"message": ...}` / `ActionResponse`) becomes the SPA toast message
17
+ - `Textarea(collapse_chars=N)` widget helper — readonly preview + “Show full text” dialog
18
+ - Unified Starlette ASGI admin core; FastAPI/Litestar as thin mount adapters
19
+ - `run_action` enforces `allow_empty` / toolbar empty selection and `requires_confirmation` / `confirmed`
20
+
21
+ ## 0.3.0 — 2026-10-01
22
+
23
+ ### Breaking changes
24
+ - `ModelAdmin.get_queryset_filters()` renamed to **`get_list_scope_filters()`** — ORM-agnostic dict filters for `repository.list(filters=...)`
25
+ - Bulk action signature: third argument is **`objects`** (list of instances), not `queryset`
26
+ - `ChangeList.get_queryset_params()` renamed to **`get_list_params()`** (internal)
27
+ - Removed oauth2-proxy / edge-cookie PoC (`ExternalSessionAuthBackend`, trust-center, proxy headers, `infra/keycloak-oauth2-poc`); SSO is **gateway web handoff only**
28
+
29
+ ### Added
30
+ - Global staff gate: non-`is_staff` users cannot enter admin (login 403 / Access Denied stub; `/me` and `/logout` still work for identity)
31
+ - `GatewayWebAuthBackend` + `GatewayWebAuthConfig` — Keycloak SSO via shared gateway (HMAC `state=web:<app_id>:…`, HTTP handoff redeem, local `sessionid`)
32
+ - `IdentityProvisionConfig` / `resolve_user_from_identity` for JIT user mapping from gateway claims
33
+ - Routes `GET /admin/sso/login`, `GET /admin/sso/complete` (Litestar + FastAPI)
34
+ - `/admin/config` auth flags: `login_url`, `session_auth`, `logout_url`
35
+ - Example: `examples/tortoise/app_gateway_web.py`
36
+ - `resolve_list_scope_filters()` helper in `litedmin.backend.admin.scope`
37
+ - `ListEditableSaveResponse.skipped` — rows skipped due to missing object or `has_change_permission`
38
+ - Row-level scope applied to autocomplete options on related models
39
+ - Swappable `AUTH_USER_MODEL` / `auth_user_model`, CLI `createsuperuser`
40
+ - `PATCH /admin/{model}/{id}` for partial updates by primary key
41
+ - Enhanced rich text editor with media uploads
42
+ - JSON tree view for form values (wrap, copy, dialog for long strings)
43
+ - Changelist query projection to `list_display` columns only
44
+ - View-only users can open list rows in read-only form mode
45
+ - E2E tests: foreign PUT 403, export scope, list_editable skip, comment/post autocomplete scope
46
+
47
+ ### Changed
48
+ - Optional ORM / framework extras are lazy at import time
49
+ - List and autocomplete pagination aligned; dropped fake fetch-all page sizes
50
+ - Auth forms, SSO profile sync, and create-form field filtering improved
51
+ - Dashboard layout widened; disabled fields lightened
52
+
53
+ ### Fixed
54
+ - `save_list_editable` now checks `has_change_permission(request, obj)` per row
55
+ - Session `expire_date` timezone handling for SQLAlchemy/asyncpg
56
+ - FastAPI admin: SPA serving, missing routes, and datetime writes
57
+ - String primary keys in admin instance routes and inline formset load/save
58
+ - List/autocomplete primary key resolution beyond attribute `id`
59
+ - Autocomplete `selected_ids` parsing ignores null values
60
+ - Admin SPA boot and existing inline rows
61
+ - Standalone ASGI run mode
62
+
63
+ ## 0.2.0 — 2026-07-06
64
+
65
+ ### Added
66
+ - `@display` decorator for computed list columns with optional ORM sorter
67
+ - CSV/JSON export with `has_export_permission` and list UI export buttons
68
+ - `StorageBackend` protocol and `ModelAdmin.upload_file` / `get_file_url` hooks
69
+ - File upload endpoint `POST /{model}/{id}/upload/{field}` and `file_upload` form widget
70
+ - Reference `LocalFileStorage` in `examples/shared/local_storage.py`
71
+ - Pytest markers, CI workflow, expanded test suite
72
+
73
+ ### Changed
74
+ - `ChangeList.get_full_response` is async (serializes `@display` columns)
75
+ - `ModelPermissionsDTO` includes `export` permission
76
+ - `OrmSettings` / app-owned DB initialization (from prior refactor)
litedmin-0.4.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mikhail Logunov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,10 @@
1
+ recursive-include litedmin/static *
2
+ include litedmin/py.typed
3
+ include README.md
4
+ include LICENSE
5
+ include CHANGELOG.md
6
+ prune examples
7
+ prune frontend
8
+ prune scripts
9
+ prune tests
10
+ prune .pytest_cache
@@ -0,0 +1,302 @@
1
+ Metadata-Version: 2.4
2
+ Name: litedmin
3
+ Version: 0.4.0
4
+ Summary: Modern admin interface inspired by Django Admin with React Material UI frontend
5
+ Author-email: Mikhail Logunov <milog7890@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ggb0yyyyyyy/litedmin
8
+ Project-URL: Repository, https://github.com/ggb0yyyyyyy/litedmin
9
+ Project-URL: Issues, https://github.com/ggb0yyyyyyy/litedmin/issues
10
+ Project-URL: Documentation, https://github.com/ggb0yyyyyyy/litedmin#readme
11
+ Keywords: admin,interface,django,react,material-ui,litestar,fastapi,sqlalchemy,tortoise
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Framework :: AsyncIO
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: msgspec>=0.18.0
30
+ Requires-Dist: bcrypt>=4.0.0
31
+ Requires-Dist: starlette>=0.36.0
32
+ Requires-Dist: python-multipart>=0.0.6
33
+ Provides-Extra: tortoise
34
+ Requires-Dist: tortoise-orm>=0.20.0; extra == "tortoise"
35
+ Requires-Dist: aiosqlite>=0.16.0; extra == "tortoise"
36
+ Provides-Extra: sqlalchemy
37
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.0; extra == "sqlalchemy"
38
+ Requires-Dist: aiosqlite>=0.16.0; extra == "sqlalchemy"
39
+ Requires-Dist: greenlet>=3.0.0; extra == "sqlalchemy"
40
+ Provides-Extra: litestar
41
+ Requires-Dist: litestar>=2.0.0; extra == "litestar"
42
+ Requires-Dist: uvicorn>=0.24.0; extra == "litestar"
43
+ Requires-Dist: httpx>=0.24.0; extra == "litestar"
44
+ Provides-Extra: fastapi
45
+ Requires-Dist: fastapi>=0.104.0; extra == "fastapi"
46
+ Requires-Dist: uvicorn>=0.24.0; extra == "fastapi"
47
+ Requires-Dist: python-multipart>=0.0.6; extra == "fastapi"
48
+ Requires-Dist: dishka>=1.2.0; extra == "fastapi"
49
+ Requires-Dist: httpx>=0.24.0; extra == "fastapi"
50
+ Provides-Extra: dev
51
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
52
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
53
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
54
+ Requires-Dist: black>=23.0.0; extra == "dev"
55
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
56
+ Requires-Dist: httpx>=0.24.0; extra == "dev"
57
+ Provides-Extra: all
58
+ Requires-Dist: litedmin[dev,fastapi,litestar,sqlalchemy,tortoise]; extra == "all"
59
+ Dynamic: license-file
60
+
61
+ # LiteAdmin
62
+
63
+ [![PyPI](https://img.shields.io/pypi/v/litedmin.svg)](https://pypi.org/project/litedmin/)
64
+ [![Python](https://img.shields.io/pypi/pyversions/litedmin.svg)](https://pypi.org/project/litedmin/)
65
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
66
+
67
+ Django Admin–style admin panel for **Litestar** and **FastAPI**, with **Tortoise ORM** and **SQLAlchemy**.
68
+
69
+ React UI ships in `litedmin/static/` — **no npm** needed for normal use.
70
+
71
+ ## Install
72
+
73
+ ```bash
74
+ pip install litedmin
75
+ pip install "litedmin[fastapi,sqlalchemy]" # FastAPI + SQLAlchemy
76
+ pip install "litedmin[litestar,tortoise]" # Litestar + Tortoise
77
+ pip install "litedmin[all]" # both ORMs + frameworks + dev tools
78
+ ```
79
+
80
+ Core symbols (`AdminConfig`, `OrmSettings`, `register`, `ModelAdmin`) import without ORM/framework extras.
81
+ ORM and web stacks load only when you import their modules (or touch lazy attributes on `litedmin`).
82
+
83
+ ### From source (examples / development)
84
+
85
+ ```bash
86
+ git clone https://github.com/ggb0yyyyyyy/litedmin.git
87
+ cd litedmin
88
+ pip install -e ".[litestar,tortoise]" # examples/tortoise (port 8000)
89
+ pip install -e ".[litestar,sqlalchemy]" # examples/sqlalchemy (port 8011)
90
+ pip install -e ".[fastapi,tortoise]"
91
+ pip install -e ".[all]"
92
+ ```
93
+
94
+ ## Repository layout
95
+
96
+ ```
97
+ liteDmin/
98
+ litedmin/ installable package
99
+ backend/
100
+ core/ AdminSite, config, OrmSettings, decorators
101
+ admin/ ModelAdmin, changelist, formsets, permissions
102
+ api/ AdminAPI / endpoints
103
+ auth/ session, backends, UserAdminMixin, system models
104
+ orm/ Tortoise / SQLAlchemy backends + repositories
105
+ integration/ Litestar & FastAPI Admin mounts, DI helpers
106
+ dto/ storage/
107
+ static/ bundled React UI
108
+ frontend/ React sources (rebuild → litedmin/static/)
109
+ examples/ Litestar + Tortoise / SQLAlchemy demos
110
+ tests/
111
+ ```
112
+
113
+ ## Quick start (Litestar + Tortoise)
114
+
115
+ Superuser is **not** created on app startup. Create it once via CLI, then run the example:
116
+
117
+ ```bash
118
+ # optional credentials for CLI
119
+ export ADMIN_USERNAME=admin
120
+ export ADMIN_PASSWORD=admin
121
+
122
+ py -3.11 -m litedmin createsuperuser --settings examples.tortoise.settings:orm_settings
123
+ py -3.11 -m uvicorn examples.tortoise.app:app --host 0.0.0.0 --port 8000
124
+ ```
125
+
126
+ Open http://localhost:8000/admin
127
+
128
+ SQLAlchemy example: same flow with `examples.sqlalchemy.settings:orm_settings` and port **8011**.
129
+ Details: [examples/README.md](examples/README.md), [examples/CREDENTIALS.md](examples/CREDENTIALS.md).
130
+
131
+ ### Minimal wiring
132
+
133
+ ```python
134
+ from litestar import Litestar, get
135
+
136
+ # Shared Starlette Admin ASGI app; Litestar/FastAPI only provide mount helpers.
137
+ from litedmin.backend.integration.litestar import Admin
138
+ from litedmin.backend.integration import init_tortoise_with_litedmin, close_tortoise
139
+ # FastAPI: from litedmin.backend.integration.fastapi import Admin
140
+ # or: from litedmin import Admin
141
+ # Then: app.mount(admin.prefix, admin.as_asgi_app())
142
+
143
+ from examples.tortoise.admin_config import ExampleAdminConfig
144
+ from examples.tortoise.settings import AUTO_DISCOVER_MODULES, orm_settings
145
+
146
+ admin = Admin(
147
+ prefix='/admin',
148
+ config=ExampleAdminConfig(), # AdminConfig: theme / UI / auth_user_model
149
+ auto_discover_modules=AUTO_DISCOVER_MODULES,
150
+ )
151
+
152
+ async def on_startup():
153
+ await init_tortoise_with_litedmin(orm_settings) # OrmSettings: ORM + DB
154
+ await admin.ensure_started() # validate admin + sync permissions (no user seed)
155
+
156
+ async def on_shutdown():
157
+ await close_tortoise()
158
+
159
+ @get('/')
160
+ async def root() -> dict:
161
+ return {'admin': '/admin'}
162
+
163
+ app = Litestar(
164
+ route_handlers=[root, admin.as_mount()],
165
+ on_startup=[on_startup],
166
+ on_shutdown=[on_shutdown],
167
+ )
168
+ ```
169
+
170
+ ### File / rich-text media storage
171
+
172
+ Pass any `StorageBackend` (`save` / `delete` / `url`) into `Admin(storage=...)`. Rich editor uploads go to `POST /admin/media/upload` and embed the returned `url` in HTML.
173
+
174
+ Examples use `LocalFileStorage` + `/media` static route — see [examples/README.md](examples/README.md). Swap in S3/MinIO by implementing the same protocol in your app.
175
+
176
+ SQLAlchemy apps also call `admin.configure_sqlalchemy(...)` after `init_sqlalchemy_with_litedmin_async` — see `examples/sqlalchemy/app.py`.
177
+
178
+ ## ModelAdmin
179
+
180
+ - Prefer **`TortoiseModelAdmin`** / **`SQLAlchemyModelAdmin`** (repository save/delete).
181
+ - Register with `@register` / `AdminSite.register` — **`admin_site` is always injected**; do not construct ModelAdmin without a site.
182
+ - Permission / persist hooks are **`async`** (`has_*`, `get_permissions`, `get_list_scope_filters`, `save_model`, `delete_model`, `process_extra_fields`). Sync getters (`get_list_display`, …) stay sync.
183
+ - Users with password field: **`UserAdminMixin`**.
184
+ - Long readonly textareas: pass options on the widget itself (SPA preview + “Show full text” dialog):
185
+
186
+ ```python
187
+ from litedmin import Textarea
188
+
189
+ class AnalysisAdmin(SQLAlchemyModelAdmin):
190
+ widgets = {
191
+ 'error': Textarea(collapse_chars=280),
192
+ }
193
+ ```
194
+
195
+ ### Field references (SQLAlchemy)
196
+
197
+ Settings accept plain names (`'title'`, `'author__username'`) for any ORM. For SQLAlchemy you can also pass the model's own attributes (and `Link(...)` for relation paths), so a type checker catches a misspelt field:
198
+
199
+ ```python
200
+ from litedmin import Link, action, display, register
201
+ from litedmin.backend.orm.sqlalchemy import SQLAlchemyModelAdmin
202
+
203
+ @register(Post)
204
+ class PostAdmin(SQLAlchemyModelAdmin):
205
+ list_display = [Post.id, Post.title, Link(Post.author, User.username), 'title_upper']
206
+ search_fields = [Post.title, Link(Post.author, User.email)]
207
+ ordering = [Post.created_at.desc()]
208
+ list_filter = [Post.published, Post.status]
209
+ ```
210
+
211
+ Tortoise models have no field attributes on the class — keep using strings there (`'title'`, `'author__username'`). Computed `@display` columns are always strings.
212
+
213
+ ### Tortoise example
214
+
215
+ ```python
216
+ from litedmin import action, register, display
217
+ from litedmin.backend.orm.tortoise import TortoiseModelAdmin
218
+
219
+ @action(description='Publish selected', row=True)
220
+ async def make_published(modeladmin, request, objects):
221
+ # ``objects`` is a list of instances (not an ORM queryset)
222
+ ...
223
+ # Optional: return a status string for the SPA toast (e.g. background job accepted)
224
+ # return 'Export queued'
225
+
226
+ @action(description='Refresh cache', toolbar=True, bulk=False, row=False)
227
+ async def refresh_cache(modeladmin, request, objects):
228
+ # Page toolbar button (no selection); same POST /{model}/action with ids=[]
229
+ ...
230
+ return 'Refresh queued'
231
+
232
+ @register(Post)
233
+ class PostAdmin(TortoiseModelAdmin):
234
+ list_display = ['title', 'title_upper']
235
+ actions = [make_published, refresh_cache] # each entry must use @action
236
+
237
+ @display(label='Upper title')
238
+ def title_upper(self, obj):
239
+ return obj.title.upper()
240
+
241
+ async def has_export_permission(self, request=None, obj=None):
242
+ return True
243
+ ```
244
+
245
+ List actions require **`@action`** (Django `@admin.action` style). Use `row=True` for per-row buttons; `bulk=False` to hide from the bulk dropdown; `toolbar=True` for page-level tools above the list (always empty selection). Signature is always `(modeladmin, request, objects)`.
246
+
247
+ ## Auth
248
+
249
+ - Default: **session cookie** login (`SessionAuthBackend` + `SessionAuth`).
250
+ - SSO (Keycloak gateway handoff): `GatewayWebAuthBackend` — see `examples/tortoise/app_gateway_web.py` and [examples/README.md](examples/README.md).
251
+
252
+ ## CLI
253
+
254
+ ```bash
255
+ litedmin createsuperuser --settings examples.tortoise.settings:orm_settings
256
+ # or
257
+ python -m litedmin createsuperuser --settings module.path:orm_settings
258
+ ```
259
+
260
+ `--username` / `--password` / `--email`, or `ADMIN_USERNAME` / `ADMIN_PASSWORD` / `ADMIN_EMAIL`.
261
+
262
+ ## Config split
263
+
264
+ | Object | Role |
265
+ |--------|------|
266
+ | **`AdminConfig`** | Theme, UI, sidebar groups, optional `auth_user_model` |
267
+ | **`OrmSettings`** | `orm_type`, database URL, app modules for ORM init |
268
+
269
+ ## Features
270
+
271
+ - `@register` / `AdminSite.register` ModelAdmin API
272
+ - SQLAlchemy field attributes / `Link(...)` in ModelAdmin settings (strings for Tortoise)
273
+ - `@display` computed list columns
274
+ - `@action` list actions (bulk dropdown + optional `row=True` buttons)
275
+ - CSV/JSON export (`has_export_permission`)
276
+ - Django-like permissions, groups, LogEntry audit trail
277
+ - `list_editable`, `date_hierarchy`, bulk actions, inlines
278
+ - Row-level scope via `get_list_scope_filters` + object-level `has_*`
279
+ - Double-submit CSRF cookie for session auth (`AdminConfig.csrf_protect`)
280
+ - `StorageBackend` protocol for file uploads
281
+
282
+ ## Typing
283
+
284
+ Package is typed; project mypy config is in `pyproject.toml` (`[tool.mypy]`).
285
+
286
+ ```bash
287
+ pip install -e ".[dev]"
288
+ python -m mypy litedmin
289
+ ```
290
+
291
+ ## Change the React UI
292
+
293
+ ```bash
294
+ pip install -e ".[all]"
295
+ # or: pip install -r requirements-dev.txt
296
+ bash scripts/build_frontend.sh
297
+ git add litedmin/static/
298
+ ```
299
+
300
+ ## License
301
+
302
+ MIT
@@ -0,0 +1,242 @@
1
+ # LiteAdmin
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/litedmin.svg)](https://pypi.org/project/litedmin/)
4
+ [![Python](https://img.shields.io/pypi/pyversions/litedmin.svg)](https://pypi.org/project/litedmin/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ Django Admin–style admin panel for **Litestar** and **FastAPI**, with **Tortoise ORM** and **SQLAlchemy**.
8
+
9
+ React UI ships in `litedmin/static/` — **no npm** needed for normal use.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install litedmin
15
+ pip install "litedmin[fastapi,sqlalchemy]" # FastAPI + SQLAlchemy
16
+ pip install "litedmin[litestar,tortoise]" # Litestar + Tortoise
17
+ pip install "litedmin[all]" # both ORMs + frameworks + dev tools
18
+ ```
19
+
20
+ Core symbols (`AdminConfig`, `OrmSettings`, `register`, `ModelAdmin`) import without ORM/framework extras.
21
+ ORM and web stacks load only when you import their modules (or touch lazy attributes on `litedmin`).
22
+
23
+ ### From source (examples / development)
24
+
25
+ ```bash
26
+ git clone https://github.com/ggb0yyyyyyy/litedmin.git
27
+ cd litedmin
28
+ pip install -e ".[litestar,tortoise]" # examples/tortoise (port 8000)
29
+ pip install -e ".[litestar,sqlalchemy]" # examples/sqlalchemy (port 8011)
30
+ pip install -e ".[fastapi,tortoise]"
31
+ pip install -e ".[all]"
32
+ ```
33
+
34
+ ## Repository layout
35
+
36
+ ```
37
+ liteDmin/
38
+ litedmin/ installable package
39
+ backend/
40
+ core/ AdminSite, config, OrmSettings, decorators
41
+ admin/ ModelAdmin, changelist, formsets, permissions
42
+ api/ AdminAPI / endpoints
43
+ auth/ session, backends, UserAdminMixin, system models
44
+ orm/ Tortoise / SQLAlchemy backends + repositories
45
+ integration/ Litestar & FastAPI Admin mounts, DI helpers
46
+ dto/ storage/
47
+ static/ bundled React UI
48
+ frontend/ React sources (rebuild → litedmin/static/)
49
+ examples/ Litestar + Tortoise / SQLAlchemy demos
50
+ tests/
51
+ ```
52
+
53
+ ## Quick start (Litestar + Tortoise)
54
+
55
+ Superuser is **not** created on app startup. Create it once via CLI, then run the example:
56
+
57
+ ```bash
58
+ # optional credentials for CLI
59
+ export ADMIN_USERNAME=admin
60
+ export ADMIN_PASSWORD=admin
61
+
62
+ py -3.11 -m litedmin createsuperuser --settings examples.tortoise.settings:orm_settings
63
+ py -3.11 -m uvicorn examples.tortoise.app:app --host 0.0.0.0 --port 8000
64
+ ```
65
+
66
+ Open http://localhost:8000/admin
67
+
68
+ SQLAlchemy example: same flow with `examples.sqlalchemy.settings:orm_settings` and port **8011**.
69
+ Details: [examples/README.md](examples/README.md), [examples/CREDENTIALS.md](examples/CREDENTIALS.md).
70
+
71
+ ### Minimal wiring
72
+
73
+ ```python
74
+ from litestar import Litestar, get
75
+
76
+ # Shared Starlette Admin ASGI app; Litestar/FastAPI only provide mount helpers.
77
+ from litedmin.backend.integration.litestar import Admin
78
+ from litedmin.backend.integration import init_tortoise_with_litedmin, close_tortoise
79
+ # FastAPI: from litedmin.backend.integration.fastapi import Admin
80
+ # or: from litedmin import Admin
81
+ # Then: app.mount(admin.prefix, admin.as_asgi_app())
82
+
83
+ from examples.tortoise.admin_config import ExampleAdminConfig
84
+ from examples.tortoise.settings import AUTO_DISCOVER_MODULES, orm_settings
85
+
86
+ admin = Admin(
87
+ prefix='/admin',
88
+ config=ExampleAdminConfig(), # AdminConfig: theme / UI / auth_user_model
89
+ auto_discover_modules=AUTO_DISCOVER_MODULES,
90
+ )
91
+
92
+ async def on_startup():
93
+ await init_tortoise_with_litedmin(orm_settings) # OrmSettings: ORM + DB
94
+ await admin.ensure_started() # validate admin + sync permissions (no user seed)
95
+
96
+ async def on_shutdown():
97
+ await close_tortoise()
98
+
99
+ @get('/')
100
+ async def root() -> dict:
101
+ return {'admin': '/admin'}
102
+
103
+ app = Litestar(
104
+ route_handlers=[root, admin.as_mount()],
105
+ on_startup=[on_startup],
106
+ on_shutdown=[on_shutdown],
107
+ )
108
+ ```
109
+
110
+ ### File / rich-text media storage
111
+
112
+ Pass any `StorageBackend` (`save` / `delete` / `url`) into `Admin(storage=...)`. Rich editor uploads go to `POST /admin/media/upload` and embed the returned `url` in HTML.
113
+
114
+ Examples use `LocalFileStorage` + `/media` static route — see [examples/README.md](examples/README.md). Swap in S3/MinIO by implementing the same protocol in your app.
115
+
116
+ SQLAlchemy apps also call `admin.configure_sqlalchemy(...)` after `init_sqlalchemy_with_litedmin_async` — see `examples/sqlalchemy/app.py`.
117
+
118
+ ## ModelAdmin
119
+
120
+ - Prefer **`TortoiseModelAdmin`** / **`SQLAlchemyModelAdmin`** (repository save/delete).
121
+ - Register with `@register` / `AdminSite.register` — **`admin_site` is always injected**; do not construct ModelAdmin without a site.
122
+ - Permission / persist hooks are **`async`** (`has_*`, `get_permissions`, `get_list_scope_filters`, `save_model`, `delete_model`, `process_extra_fields`). Sync getters (`get_list_display`, …) stay sync.
123
+ - Users with password field: **`UserAdminMixin`**.
124
+ - Long readonly textareas: pass options on the widget itself (SPA preview + “Show full text” dialog):
125
+
126
+ ```python
127
+ from litedmin import Textarea
128
+
129
+ class AnalysisAdmin(SQLAlchemyModelAdmin):
130
+ widgets = {
131
+ 'error': Textarea(collapse_chars=280),
132
+ }
133
+ ```
134
+
135
+ ### Field references (SQLAlchemy)
136
+
137
+ Settings accept plain names (`'title'`, `'author__username'`) for any ORM. For SQLAlchemy you can also pass the model's own attributes (and `Link(...)` for relation paths), so a type checker catches a misspelt field:
138
+
139
+ ```python
140
+ from litedmin import Link, action, display, register
141
+ from litedmin.backend.orm.sqlalchemy import SQLAlchemyModelAdmin
142
+
143
+ @register(Post)
144
+ class PostAdmin(SQLAlchemyModelAdmin):
145
+ list_display = [Post.id, Post.title, Link(Post.author, User.username), 'title_upper']
146
+ search_fields = [Post.title, Link(Post.author, User.email)]
147
+ ordering = [Post.created_at.desc()]
148
+ list_filter = [Post.published, Post.status]
149
+ ```
150
+
151
+ Tortoise models have no field attributes on the class — keep using strings there (`'title'`, `'author__username'`). Computed `@display` columns are always strings.
152
+
153
+ ### Tortoise example
154
+
155
+ ```python
156
+ from litedmin import action, register, display
157
+ from litedmin.backend.orm.tortoise import TortoiseModelAdmin
158
+
159
+ @action(description='Publish selected', row=True)
160
+ async def make_published(modeladmin, request, objects):
161
+ # ``objects`` is a list of instances (not an ORM queryset)
162
+ ...
163
+ # Optional: return a status string for the SPA toast (e.g. background job accepted)
164
+ # return 'Export queued'
165
+
166
+ @action(description='Refresh cache', toolbar=True, bulk=False, row=False)
167
+ async def refresh_cache(modeladmin, request, objects):
168
+ # Page toolbar button (no selection); same POST /{model}/action with ids=[]
169
+ ...
170
+ return 'Refresh queued'
171
+
172
+ @register(Post)
173
+ class PostAdmin(TortoiseModelAdmin):
174
+ list_display = ['title', 'title_upper']
175
+ actions = [make_published, refresh_cache] # each entry must use @action
176
+
177
+ @display(label='Upper title')
178
+ def title_upper(self, obj):
179
+ return obj.title.upper()
180
+
181
+ async def has_export_permission(self, request=None, obj=None):
182
+ return True
183
+ ```
184
+
185
+ List actions require **`@action`** (Django `@admin.action` style). Use `row=True` for per-row buttons; `bulk=False` to hide from the bulk dropdown; `toolbar=True` for page-level tools above the list (always empty selection). Signature is always `(modeladmin, request, objects)`.
186
+
187
+ ## Auth
188
+
189
+ - Default: **session cookie** login (`SessionAuthBackend` + `SessionAuth`).
190
+ - SSO (Keycloak gateway handoff): `GatewayWebAuthBackend` — see `examples/tortoise/app_gateway_web.py` and [examples/README.md](examples/README.md).
191
+
192
+ ## CLI
193
+
194
+ ```bash
195
+ litedmin createsuperuser --settings examples.tortoise.settings:orm_settings
196
+ # or
197
+ python -m litedmin createsuperuser --settings module.path:orm_settings
198
+ ```
199
+
200
+ `--username` / `--password` / `--email`, or `ADMIN_USERNAME` / `ADMIN_PASSWORD` / `ADMIN_EMAIL`.
201
+
202
+ ## Config split
203
+
204
+ | Object | Role |
205
+ |--------|------|
206
+ | **`AdminConfig`** | Theme, UI, sidebar groups, optional `auth_user_model` |
207
+ | **`OrmSettings`** | `orm_type`, database URL, app modules for ORM init |
208
+
209
+ ## Features
210
+
211
+ - `@register` / `AdminSite.register` ModelAdmin API
212
+ - SQLAlchemy field attributes / `Link(...)` in ModelAdmin settings (strings for Tortoise)
213
+ - `@display` computed list columns
214
+ - `@action` list actions (bulk dropdown + optional `row=True` buttons)
215
+ - CSV/JSON export (`has_export_permission`)
216
+ - Django-like permissions, groups, LogEntry audit trail
217
+ - `list_editable`, `date_hierarchy`, bulk actions, inlines
218
+ - Row-level scope via `get_list_scope_filters` + object-level `has_*`
219
+ - Double-submit CSRF cookie for session auth (`AdminConfig.csrf_protect`)
220
+ - `StorageBackend` protocol for file uploads
221
+
222
+ ## Typing
223
+
224
+ Package is typed; project mypy config is in `pyproject.toml` (`[tool.mypy]`).
225
+
226
+ ```bash
227
+ pip install -e ".[dev]"
228
+ python -m mypy litedmin
229
+ ```
230
+
231
+ ## Change the React UI
232
+
233
+ ```bash
234
+ pip install -e ".[all]"
235
+ # or: pip install -r requirements-dev.txt
236
+ bash scripts/build_frontend.sh
237
+ git add litedmin/static/
238
+ ```
239
+
240
+ ## License
241
+
242
+ MIT