polyadmin 0.1.0b2__tar.gz → 0.1.0b3__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 (212) hide show
  1. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/PKG-INFO +4 -2
  2. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/README.md +3 -1
  3. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/deletes.md +4 -0
  4. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/models.py +2 -3
  5. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/session.py +11 -5
  6. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/tests/test_app.py +1 -1
  7. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/__init__.py +2 -0
  8. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/admin.py +2 -0
  9. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/delete.py +8 -4
  10. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/field.py +14 -5
  11. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/filter.py +0 -1
  12. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/model_admin.py +3 -0
  13. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/template_context.py +254 -86
  14. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/deletes.py +2 -2
  15. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/handlers.py +5 -5
  16. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/login.py +1 -1
  17. polyadmin-0.1.0b3/polyadmin/locale/fr/LC_MESSAGES/polyadmin.mo +0 -0
  18. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/locale/fr/LC_MESSAGES/polyadmin.po +18 -9
  19. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/locale/polyadmin.pot +7 -7
  20. polyadmin-0.1.0b3/polyadmin/locale/ru/LC_MESSAGES/polyadmin.mo +0 -0
  21. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/locale/ru/LC_MESSAGES/polyadmin.po +18 -9
  22. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/base.html +1 -0
  23. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/field.html +12 -0
  24. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/icons.html +1 -0
  25. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/inline.html +18 -8
  26. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/field.html +1 -0
  27. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/login.html +8 -4
  28. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/theme.html +5 -0
  29. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/ui.py +21 -47
  30. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/pyproject.toml +1 -1
  31. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_delete.py +12 -11
  32. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_i18n.py +2 -3
  33. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_template_context.py +24 -0
  34. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_actions.py +1 -1
  35. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_components.py +0 -4
  36. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_delete_selected.py +0 -1
  37. polyadmin-0.1.0b3/tests/fastapi/test_favicon.py +35 -0
  38. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_login.py +17 -2
  39. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_router.py +1 -1
  40. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_scroll_area.py +60 -1
  41. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_switcher.py +2 -2
  42. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_format.py +48 -1
  43. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_templating.py +4 -4
  44. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_ui.py +2 -2
  45. polyadmin-0.1.0b2/polyadmin/locale/fr/LC_MESSAGES/polyadmin.mo +0 -0
  46. polyadmin-0.1.0b2/polyadmin/locale/ru/LC_MESSAGES/polyadmin.mo +0 -0
  47. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/.github/workflows/notify-docs.yml +0 -0
  48. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/.github/workflows/publish.yml +0 -0
  49. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/.gitignore +0 -0
  50. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/LICENSE +0 -0
  51. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/babel.cfg +0 -0
  52. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/README.md +0 -0
  53. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/conftest.py +0 -0
  54. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/test_deletes.py +0 -0
  55. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/test_flows.py +0 -0
  56. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/test_i18n.py +0 -0
  57. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/test_parity.py +0 -0
  58. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/architecture.md +0 -0
  59. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/audit.md +0 -0
  60. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/authentication.md +0 -0
  61. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/components.md +0 -0
  62. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/concepts/architecture.md +0 -0
  63. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/concepts/model-admin.md +0 -0
  64. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/dashboard.md +0 -0
  65. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/exports.md +0 -0
  66. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/i18n.md +0 -0
  67. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/inlines.md +0 -0
  68. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/lists.md +0 -0
  69. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/model-admin.md +0 -0
  70. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/permissions.md +0 -0
  71. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/python/index.md +0 -0
  72. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/routing.md +0 -0
  73. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/templates.md +0 -0
  74. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/README.md +0 -0
  75. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/README.md +0 -0
  76. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/main.py +0 -0
  77. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/organization_admin.py +0 -0
  78. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/pages.py +0 -0
  79. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/pyproject.toml +0 -0
  80. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/role_admin.py +0 -0
  81. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/templates/pages/broadcast.html +0 -0
  82. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/user_admin.py +0 -0
  83. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/uv.lock +0 -0
  84. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/mkdocs.yml +0 -0
  85. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/__init__.py +0 -0
  86. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/_async.py +0 -0
  87. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/action.py +0 -0
  88. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/audit.py +0 -0
  89. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/auth.py +0 -0
  90. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/authorization.py +0 -0
  91. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/csrf.py +0 -0
  92. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/dashboard.py +0 -0
  93. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/exporter.py +0 -0
  94. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/inline.py +0 -0
  95. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/login.py +0 -0
  96. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/page.py +0 -0
  97. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/pagination.py +0 -0
  98. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/query.py +0 -0
  99. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/relation.py +0 -0
  100. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/slug.py +0 -0
  101. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/widget.py +0 -0
  102. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/__init__.py +0 -0
  103. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/audit.py +0 -0
  104. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/auth.py +0 -0
  105. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/csrf.py +0 -0
  106. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/errors.py +0 -0
  107. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/inlines.py +0 -0
  108. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/locale.py +0 -0
  109. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/pages.py +0 -0
  110. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/relations.py +0 -0
  111. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/responses.py +0 -0
  112. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/router.py +0 -0
  113. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/static.py +0 -0
  114. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/__init__.py +0 -0
  115. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/context.py +0 -0
  116. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/negotiation.py +0 -0
  117. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/setup.py +0 -0
  118. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/translator.py +0 -0
  119. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/action_confirm_modal.html +0 -0
  120. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/csrf-field.html +0 -0
  121. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/error_fragment.html +0 -0
  122. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/form_wrapper.html +0 -0
  123. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/inline_fragment.html +0 -0
  124. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/list_content.html +0 -0
  125. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/lookup_results.html +0 -0
  126. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/search.html +0 -0
  127. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/toasts.html +0 -0
  128. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/breadcrumb.html +0 -0
  129. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/bulk-actions.html +0 -0
  130. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/calendar.html +0 -0
  131. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/combobox.html +0 -0
  132. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/delete-preview.html +0 -0
  133. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/dropdown-menu.html +0 -0
  134. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/filter-panel.html +0 -0
  135. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/locale-switcher.html +0 -0
  136. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/multi-select.html +0 -0
  137. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/pagination.html +0 -0
  138. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/radio-group.html +0 -0
  139. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/select.html +0 -0
  140. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/sidebar.html +0 -0
  141. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/slider.html +0 -0
  142. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/switch.html +0 -0
  143. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/table.html +0 -0
  144. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/theme-toggle.html +0 -0
  145. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/dashboard.html +0 -0
  146. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/error.html +0 -0
  147. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/delete.html +0 -0
  148. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/delete_selected.html +0 -0
  149. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/detail.html +0 -0
  150. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/form.html +0 -0
  151. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/list.html +0 -0
  152. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/activity.html +0 -0
  153. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/chart.html +0 -0
  154. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/donut.html +0 -0
  155. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/metric.html +0 -0
  156. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/progress.html +0 -0
  157. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/stat.html +0 -0
  158. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/table.html +0 -0
  159. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/tabs.html +0 -0
  160. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/timeline.html +0 -0
  161. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templating.py +0 -0
  162. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/__init__.py +0 -0
  163. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/conftest.py +0 -0
  164. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/__init__.py +0 -0
  165. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/mo.py +0 -0
  166. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_action_decorator.py +0 -0
  167. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_action_placement.py +0 -0
  168. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_admin.py +0 -0
  169. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_async.py +0 -0
  170. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_auth.py +0 -0
  171. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_authorization.py +0 -0
  172. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_csrf.py +0 -0
  173. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_dashboard.py +0 -0
  174. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_delete_selected_i18n.py +0 -0
  175. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_exporter.py +0 -0
  176. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_field.py +0 -0
  177. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_filter.py +0 -0
  178. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_inline.py +0 -0
  179. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_login.py +0 -0
  180. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_model_admin.py +0 -0
  181. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_pagination.py +0 -0
  182. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_parity.py +0 -0
  183. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_query.py +0 -0
  184. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_relation.py +0 -0
  185. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_widget.py +0 -0
  186. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/__init__.py +0 -0
  187. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_action_placement.py +0 -0
  188. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_async_model_admin.py +0 -0
  189. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_async_relations.py +0 -0
  190. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_audit.py +0 -0
  191. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_auth.py +0 -0
  192. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_code_strings.py +0 -0
  193. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_csrf.py +0 -0
  194. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_dashboard.py +0 -0
  195. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_delete_preview.py +0 -0
  196. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_delete_selected_preview.py +0 -0
  197. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_errors.py +0 -0
  198. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_export.py +0 -0
  199. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_filters.py +0 -0
  200. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_htmx.py +0 -0
  201. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_inlines.py +0 -0
  202. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_list_querier.py +0 -0
  203. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_listopts.py +0 -0
  204. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_locale.py +0 -0
  205. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_pages.py +0 -0
  206. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_parity_list.py +0 -0
  207. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_pseudo_locale.py +0 -0
  208. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_relations.py +0 -0
  209. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_theme.py +0 -0
  210. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_catalogs.py +0 -0
  211. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_layout.py +0 -0
  212. {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: polyadmin
3
- Version: 0.1.0b2
3
+ Version: 0.1.0b3
4
4
  Summary: PolyAdmin: cross-language server-rendered admin framework (Python core)
5
5
  Project-URL: Documentation, https://magicrodri.github.io/polyadmin-docs/
6
6
  Project-URL: Source, https://github.com/MagicRodri/polyadmin
@@ -59,6 +59,7 @@ permissions) and you get:
59
59
  - Relation fields rendered as links, or as a searchable shadcn/ui
60
60
  Command-style autocomplete backed by a server-side `/lookup` route
61
61
  (never dumps the target model's full queryset into the page)
62
+ - Image fields with upload handling and preview rendering
62
63
  - Inline related records (`StackedInline`/`TabularInline`) — a
63
64
  parent's create/detail/edit pages can show and manage a child's
64
65
  records that point back at it, directly within the parent workflow
@@ -73,6 +74,7 @@ permissions) and you get:
73
74
  - CSV and XLSX export
74
75
  - Internationalisation: per-request locale, language switcher, French
75
76
  and Russian
77
+ - Built-in favicon support for the admin shell
76
78
  - Toast notifications for every create/update/delete/action
77
79
  - Per-resource (and per-widget) template overrides, so an application
78
80
  can replace one view's markup without forking the framework
@@ -92,7 +94,7 @@ See [`docs/components.md`](docs/components.md).
92
94
  The first beta release is available on PyPI:
93
95
 
94
96
  ```bash
95
- pip install "polyadmin[fastapi]==0.1.0b2"
97
+ pip install "polyadmin[fastapi]==0.1.0b3"
96
98
  ```
97
99
 
98
100
  For the latest unreleased code, install from Git:
@@ -29,6 +29,7 @@ permissions) and you get:
29
29
  - Relation fields rendered as links, or as a searchable shadcn/ui
30
30
  Command-style autocomplete backed by a server-side `/lookup` route
31
31
  (never dumps the target model's full queryset into the page)
32
+ - Image fields with upload handling and preview rendering
32
33
  - Inline related records (`StackedInline`/`TabularInline`) — a
33
34
  parent's create/detail/edit pages can show and manage a child's
34
35
  records that point back at it, directly within the parent workflow
@@ -43,6 +44,7 @@ permissions) and you get:
43
44
  - CSV and XLSX export
44
45
  - Internationalisation: per-request locale, language switcher, French
45
46
  and Russian
47
+ - Built-in favicon support for the admin shell
46
48
  - Toast notifications for every create/update/delete/action
47
49
  - Per-resource (and per-widget) template overrides, so an application
48
50
  can replace one view's markup without forking the framework
@@ -62,7 +64,7 @@ See [`docs/components.md`](docs/components.md).
62
64
  The first beta release is available on PyPI:
63
65
 
64
66
  ```bash
65
- pip install "polyadmin[fastapi]==0.1.0b2"
67
+ pip install "polyadmin[fastapi]==0.1.0b3"
66
68
  ```
67
69
 
68
70
  For the latest unreleased code, install from Git:
@@ -39,6 +39,10 @@ class DeletePreviewer(Protocol):
39
39
  def delete_preview(self, objects: list[Any]) -> DeletePreview: ...
40
40
  ```
41
41
 
42
+ It may be a coroutine function — the same optional-async shape as
43
+ `get_object`/`create`/`update`/`delete` — for a ModelAdmin backed by an
44
+ async client that needs to look up the cascade over the network.
45
+
42
46
  `objects` is always a list — one record from the delete page, the whole
43
47
  selection from `delete_selected` — so one implementation serves both,
44
48
  and a bulk delete costs one query per relation rather than one per row:
@@ -163,9 +163,8 @@ def seed(
163
163
  organizations: OrganizationRepository,
164
164
  roles: RoleRepository,
165
165
  ) -> None:
166
- # Founded in the same month for every organization: Task 15's browser
167
- # test checks that this date renders with a French month name under
168
- # the fr locale, and it doesn't matter which organization it looks at.
166
+ # A browser test checks that this date renders with a French month name
167
+ # under the fr locale, regardless of which organization it looks at.
169
168
  # Acme keeps March 2019: a browser test pins "Mar 1, 2019" as a data
170
169
  # value and checks the same date renders "mars" under fr. The others
171
170
  # spread out, so the date drill-down has years and months to walk.
@@ -40,6 +40,7 @@ class DemoAccount:
40
40
  """One row of what would be a users table."""
41
41
 
42
42
  email: str
43
+ username: str
43
44
  # Salt and hash, never the password. Derived at import time only
44
45
  # because a runnable demo has to document its own credentials; a real
45
46
  # table stores these and has never seen the plaintext.
@@ -55,9 +56,9 @@ class DemoAccount:
55
56
  # whose preferred locale the host resolver -- not the switcher cookie or
56
57
  # Accept-Language -- decides.
57
58
  DEMO_CREDENTIALS = [
58
- ("admin@example.com", "polyadmin", "Demo Admin", True, ""),
59
- ("viewer@example.com", "polyadmin", "Demo Viewer", False, ""),
60
- ("amelie@example.com", "polyadmin", "Amélie", True, "fr"),
59
+ ("admin@example.com", "admin", "polyadmin", "Demo Admin", True, ""),
60
+ ("viewer@example.com", "viewer", "polyadmin", "Demo Viewer", False, ""),
61
+ ("amelie@example.com", "amelie", "polyadmin", "Amélie", True, "fr"),
61
62
  ]
62
63
 
63
64
 
@@ -114,16 +115,21 @@ class CookieSessionBackend:
114
115
  def __init__(self) -> None:
115
116
  self._secret = _session_secret()
116
117
  self._accounts: dict[str, DemoAccount] = {}
117
- for email, password, display_name, is_superuser, locale in DEMO_CREDENTIALS:
118
+ for email, username, password, display_name, is_superuser, locale in DEMO_CREDENTIALS:
118
119
  salt = secrets.token_bytes(16)
119
- self._accounts[email] = DemoAccount(
120
+ account = DemoAccount(
120
121
  email=email,
122
+ username=username,
121
123
  salt=salt,
122
124
  hash=_derive(password, salt),
123
125
  display_name=display_name,
124
126
  is_superuser=is_superuser,
125
127
  locale=locale,
126
128
  )
129
+ # Indexed under both so the login page's "username or email"
130
+ # field resolves either one to the same account.
131
+ self._accounts[email] = account
132
+ self._accounts[username] = account
127
133
 
128
134
  def verify_credentials(self, request: Any, identifier: str, password: str) -> Principal | None:
129
135
  """Answers "are these good?" and does not touch the response. Establishing
@@ -130,7 +130,7 @@ def test_create_organization_persists_founded_and_balance():
130
130
  location = response.headers["location"]
131
131
  detail = client.get(location)
132
132
  assert detail.status_code == 200
133
- # Rendered as the markup contract (Task 12): a <time datetime="...">
133
+ # Rendered as the markup contract: a <time datetime="...">
134
134
  # for the date and a data-value carrying the exact posted precision.
135
135
  # This exercises the real create() pipeline end to end -- Field.parse_
136
136
  # form_value (float for "decimal", raw string for "date"), the example
@@ -13,6 +13,7 @@ from polyadmin.core.field import (
13
13
  EmailField,
14
14
  EnumField,
15
15
  Field,
16
+ ImageField,
16
17
  IntegerField,
17
18
  JSONField,
18
19
  PasswordField,
@@ -36,6 +37,7 @@ __all__ = [
36
37
  "EmailField",
37
38
  "EnumField",
38
39
  "Field",
40
+ "ImageField",
39
41
  "IntegerField",
40
42
  "JSONField",
41
43
  "ModelAdmin",
@@ -28,6 +28,7 @@ class Admin:
28
28
  authorizer: Any | None = None,
29
29
  site_title: str = "PolyAdmin",
30
30
  site_logo_url: str | None = None,
31
+ site_favicon_url: str | None = None,
31
32
  disable_csrf: bool = False,
32
33
  audit_logger: Any | None = None,
33
34
  login_backend: Any | None = None,
@@ -45,6 +46,7 @@ class Admin:
45
46
  self.authorizer = authorizer
46
47
  self.site_title = site_title
47
48
  self.site_logo_url = site_logo_url
49
+ self.site_favicon_url = site_favicon_url
48
50
  # Opt-out, never opt-in: a security control that defaults to off
49
51
  # is one nobody turns on. The token cookie is still minted when
50
52
  # this is set, so templates and custom pages behave identically.
@@ -6,10 +6,11 @@ that makes a host's answer safe to show one principal.
6
6
  from __future__ import annotations
7
7
 
8
8
  import hashlib
9
- from collections.abc import Sequence
9
+ from collections.abc import Awaitable, Sequence
10
10
  from dataclasses import dataclass, field
11
11
  from typing import Any, Protocol
12
12
 
13
+ from polyadmin.core._async import maybe_await
13
14
  from polyadmin.core.authorization import resource_permission
14
15
 
15
16
  # How many records of a group are listed; the rest are counted.
@@ -46,9 +47,12 @@ class DeletePreviewer(Protocol):
46
47
  `objects` is always a list -- one record from the delete page, the whole
47
48
  selection from delete_selected -- so an implementation over SQL can
48
49
  answer a bulk delete with one count and one sample query per relation.
50
+
51
+ May be a coroutine function; resolve_delete_preview awaits it when it is
52
+ one, the same optional-async shape as get_object/create/update/delete.
49
53
  """
50
54
 
51
- def delete_preview(self, objects: list[Any]) -> DeletePreview: ...
55
+ def delete_preview(self, objects: list[Any]) -> DeletePreview | Awaitable[DeletePreview]: ...
52
56
 
53
57
 
54
58
  @dataclass
@@ -74,14 +78,14 @@ def previews_deletes(model_admin: Any) -> bool:
74
78
  return callable(getattr(model_admin, "delete_preview", None))
75
79
 
76
80
 
77
- def resolve_delete_preview(admin: Any, model_admin: Any, principal: Any, objects: Sequence[Any]) -> ResolvedDeletePreview:
81
+ async def resolve_delete_preview(admin: Any, model_admin: Any, principal: Any, objects: Sequence[Any]) -> ResolvedDeletePreview:
78
82
  """Ask model_admin what deleting objects takes with it and filter the answer
79
83
  for principal. Without the capability: an empty, non-blocking result, and
80
84
  nothing is called.
81
85
  """
82
86
  if not previews_deletes(model_admin):
83
87
  return ResolvedDeletePreview()
84
- preview = model_admin.delete_preview(list(objects))
88
+ preview = await maybe_await(model_admin.delete_preview(list(objects)))
85
89
  out = ResolvedDeletePreview()
86
90
  for group in preview.cascades:
87
91
  resolved = _resolve_group(admin, principal, group)
@@ -1,9 +1,8 @@
1
1
  """Field abstraction.
2
2
 
3
3
  A Field represents a model property plus its admin presentation, not
4
- merely an HTML input. Context-aware rendering (list/detail/form/filter)
5
- is added in Phase 2; this module defines the data-level contract that
6
- rendering will build on.
4
+ merely an HTML input. This module defines the data-level contract that
5
+ context-aware rendering (list/detail/form/filter) builds on.
7
6
  """
8
7
 
9
8
  from __future__ import annotations
@@ -87,7 +86,9 @@ class Field:
87
86
  """Run configured validators, returning a list of error messages."""
88
87
  errors: list[str] = []
89
88
  if self.required and (value is None or value == ""):
90
- errors.append(gettext("%(label)s is required.") % {"label": gettext(self.label)})
89
+ errors.append(
90
+ gettext("%(label)s is required.") % {"label": gettext(self.label)}
91
+ )
91
92
  return errors
92
93
  for validator in self.validators:
93
94
  try:
@@ -138,6 +139,10 @@ class URLField(Field):
138
139
  field_type = "url"
139
140
 
140
141
 
142
+ class ImageField(Field):
143
+ field_type = "image"
144
+
145
+
141
146
  class UUIDField(Field):
142
147
  field_type = "uuid"
143
148
 
@@ -192,5 +197,9 @@ class ManyToManyField(Field):
192
197
  self.relation = relation
193
198
 
194
199
  def get_value(self, obj: Any) -> Any:
195
- value = self.relation.get_related(obj) if self.relation.get_related is not None else super().get_value(obj)
200
+ value = (
201
+ self.relation.get_related(obj)
202
+ if self.relation.get_related is not None
203
+ else super().get_value(obj)
204
+ )
196
205
  return value if value is not None else []
@@ -299,7 +299,6 @@ class RelationFilter(Filter):
299
299
  if not many:
300
300
  if self._pk(related) == raw_value:
301
301
  kept.append(obj)
302
- # A `many` relation matches when any member does.
303
302
  elif any(self._pk(member) == raw_value for member in related):
304
303
  kept.append(obj)
305
304
  return kept
@@ -49,6 +49,9 @@ class ModelAdmin:
49
49
  # Sidebar icon name (see components/icons.html), shown flat or nested
50
50
  # inside a category's accordion alike.
51
51
  icon: ClassVar[str] = "collection"
52
+ # Browser-tab icon shown while viewing this ModelAdmin's pages. ""
53
+ # falls back to Admin.site_favicon_url.
54
+ favicon_url: ClassVar[str] = ""
52
55
 
53
56
  list_display: ClassVar[Sequence[str]] = ()
54
57
  form_fields: ClassVar[Sequence[str]] = ()