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.
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/PKG-INFO +4 -2
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/README.md +3 -1
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/deletes.md +4 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/models.py +2 -3
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/session.py +11 -5
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/tests/test_app.py +1 -1
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/__init__.py +2 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/admin.py +2 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/delete.py +8 -4
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/field.py +14 -5
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/filter.py +0 -1
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/model_admin.py +3 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/template_context.py +254 -86
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/deletes.py +2 -2
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/handlers.py +5 -5
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/login.py +1 -1
- polyadmin-0.1.0b3/polyadmin/locale/fr/LC_MESSAGES/polyadmin.mo +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/locale/fr/LC_MESSAGES/polyadmin.po +18 -9
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/locale/polyadmin.pot +7 -7
- polyadmin-0.1.0b3/polyadmin/locale/ru/LC_MESSAGES/polyadmin.mo +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/locale/ru/LC_MESSAGES/polyadmin.po +18 -9
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/base.html +1 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/field.html +12 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/icons.html +1 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/inline.html +18 -8
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/field.html +1 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/login.html +8 -4
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/theme.html +5 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/ui.py +21 -47
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/pyproject.toml +1 -1
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_delete.py +12 -11
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_i18n.py +2 -3
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_template_context.py +24 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_actions.py +1 -1
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_components.py +0 -4
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_delete_selected.py +0 -1
- polyadmin-0.1.0b3/tests/fastapi/test_favicon.py +35 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_login.py +17 -2
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_router.py +1 -1
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_scroll_area.py +60 -1
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_switcher.py +2 -2
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_format.py +48 -1
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_templating.py +4 -4
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_ui.py +2 -2
- polyadmin-0.1.0b2/polyadmin/locale/fr/LC_MESSAGES/polyadmin.mo +0 -0
- polyadmin-0.1.0b2/polyadmin/locale/ru/LC_MESSAGES/polyadmin.mo +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/.github/workflows/notify-docs.yml +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/.github/workflows/publish.yml +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/.gitignore +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/LICENSE +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/babel.cfg +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/README.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/conftest.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/test_deletes.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/test_flows.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/test_i18n.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/browsertests/test_parity.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/architecture.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/audit.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/authentication.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/components.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/concepts/architecture.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/concepts/model-admin.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/dashboard.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/exports.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/i18n.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/inlines.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/lists.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/model-admin.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/permissions.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/python/index.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/routing.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/docs/templates.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/README.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/README.md +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/main.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/organization_admin.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/pages.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/pyproject.toml +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/role_admin.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/templates/pages/broadcast.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/user_admin.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/examples/fastapi/uv.lock +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/mkdocs.yml +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/__init__.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/_async.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/action.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/audit.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/auth.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/authorization.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/csrf.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/dashboard.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/exporter.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/inline.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/login.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/page.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/pagination.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/query.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/relation.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/slug.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/core/widget.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/__init__.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/audit.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/auth.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/csrf.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/errors.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/inlines.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/locale.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/pages.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/relations.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/responses.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/router.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/fastapi/static.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/__init__.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/context.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/negotiation.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/setup.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/i18n/translator.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/action_confirm_modal.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/csrf-field.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/error_fragment.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/form_wrapper.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/inline_fragment.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/list_content.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/lookup_results.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/search.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/toasts.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/breadcrumb.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/bulk-actions.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/calendar.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/combobox.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/delete-preview.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/dropdown-menu.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/filter-panel.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/locale-switcher.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/multi-select.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/pagination.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/radio-group.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/select.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/sidebar.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/slider.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/switch.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/table.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/components/ui/theme-toggle.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/dashboard.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/error.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/delete.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/delete_selected.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/detail.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/form.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/resource/list.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/activity.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/chart.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/donut.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/metric.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/progress.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/stat.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/table.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/tabs.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templates/admin/widgets/timeline.html +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/polyadmin/templating.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/__init__.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/conftest.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/__init__.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/mo.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_action_decorator.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_action_placement.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_admin.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_async.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_auth.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_authorization.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_csrf.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_dashboard.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_delete_selected_i18n.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_exporter.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_field.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_filter.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_inline.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_login.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_model_admin.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_pagination.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_parity.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_query.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_relation.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/core/test_widget.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/__init__.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_action_placement.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_async_model_admin.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_async_relations.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_audit.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_auth.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_code_strings.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_csrf.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_dashboard.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_delete_preview.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_delete_selected_preview.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_errors.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_export.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_filters.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_htmx.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_inlines.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_list_querier.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_listopts.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_locale.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_pages.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_parity_list.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_pseudo_locale.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_relations.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/fastapi/test_theme.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_catalogs.py +0 -0
- {polyadmin-0.1.0b2 → polyadmin-0.1.0b3}/tests/test_layout.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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
|
-
#
|
|
167
|
-
#
|
|
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
|
-
|
|
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
|
|
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.
|
|
5
|
-
|
|
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(
|
|
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 =
|
|
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]] = ()
|