django-admin-mobile 0.2.4__tar.gz → 0.2.7__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.
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/CHANGELOG.md +16 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/PKG-INFO +77 -38
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/README.md +76 -37
- django_admin_mobile-0.2.7/docs/hero.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/app-index-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/app-index-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/bulk-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/bulk-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/change-form-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/change-form-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/changelist-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/changelist-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/changelist-editable-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/changelist-editable-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/favorites-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/favorites-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/filters-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/filters-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/history-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/history-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/infinite-scroll-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/infinite-scroll-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/message-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/message-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/nav-sidebar-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/nav-sidebar-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/payments-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/payments-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/payments-selected-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/payments-selected-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/pull-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/pull-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/rtl-changelist-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/rtl-changelist-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/stock-changelist-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/stock-changelist-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/swipe-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/swipe-375.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/toast-375-dark.png +0 -0
- django_admin_mobile-0.2.7/docs/screenshots/toast-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/admin.py +43 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/demo.py +12 -0
- django_admin_mobile-0.2.7/example/library/migrations/0004_payment.py +96 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/models.py +32 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/__init__.py +1 -1
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/static/admin_mobile/css/admin_mobile.css +14 -6
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/static/admin_mobile/css/admin_mobile_features.css +507 -24
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/static/admin_mobile/js/admin_mobile.js +16 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/static/admin_mobile/js/admin_mobile_features.js +186 -19
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_playwright.py +223 -3
- django_admin_mobile-0.2.4/docs/screenshots/app-index-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/app-index-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/bulk-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/bulk-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/change-form-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/change-form-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/changelist-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/changelist-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/changelist-editable-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/changelist-editable-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/favorites-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/favorites-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/filters-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/filters-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/history-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/history-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/infinite-scroll-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/infinite-scroll-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/message-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/message-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/nav-sidebar-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/nav-sidebar-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/pull-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/pull-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/rtl-changelist-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/rtl-changelist-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/swipe-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/swipe-375.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/toast-375-dark.png +0 -0
- django_admin_mobile-0.2.4/docs/screenshots/toast-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/.gitignore +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/CONTRIBUTING.md +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/LICENSE +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/admindocs-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/admindocs-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/admindocs-model-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/admindocs-model-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/calendar-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/calendar-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/camera-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/camera-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/clock-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/clock-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/delete-confirm-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/delete-confirm-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/edition-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/edition-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/index-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/index-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/inline-errors-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/inline-errors-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/lightbox-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/lightbox-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/logged-out-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/logged-out-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/login-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/login-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/navigator-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/navigator-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/password-change-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/password-change-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/picker-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/picker-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/popup-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/popup-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/readonly-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/readonly-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/recent-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/recent-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/search-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/search-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/slide-confirm-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/slide-confirm-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/tabs-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/tabs-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/undo-375-dark.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/docs/screenshots/undo-375.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/example/__init__.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/example/settings.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/example/urls.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/example/wsgi.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/__init__.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/apps.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/management/__init__.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/management/commands/__init__.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/management/commands/seed_demo.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/migrations/0001_initial.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/migrations/0002_admin_surfaces.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/migrations/0003_shelf_plural.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/library/migrations/__init__.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/example/manage.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/pyproject.toml +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/apps.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/checks.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/colors.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/conf.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/mixins.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/overrides.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/py.typed +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/readonly.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/routing.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/search.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/static/admin_mobile/icons/icon-192.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/static/admin_mobile/icons/icon-512.png +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/templates/admin/admin_mobile/recent.html +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/templates/admin/admin_mobile/search.html +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/templates/admin/base.html +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/templates/admin/change_list.html +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/templates/admin_mobile/widgets/split_datetime.html +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/templatetags/__init__.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/templatetags/admin_mobile.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/views.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/src/admin_mobile/widgets.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/__init__.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/formparse.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/settings.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_admin_save.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_checks.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_config.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_demo.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_features.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_formparse.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_mixin.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_static_contract.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_templates.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/test_widgets.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tests/urls.py +0 -0
- {django_admin_mobile-0.2.4 → django_admin_mobile-0.2.7}/tox.ini +0 -0
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.7
|
|
4
|
+
|
|
5
|
+
- The phone filter panel is a bottom sheet: dimmed backdrop, 16px top corners, grab handle, sticky Filters / Reset / Close header, chevrons, 44px options with an accent check and right-aligned counts, and a sticky Show results button. The scrim darkens the page in light and dark. A selected row keeps a visible accent tint and check in dark mode.
|
|
6
|
+
- Long filter lists get a search field. Date and range inputs stay in the sheet. With JavaScript off, the filters stay inline.
|
|
7
|
+
|
|
8
|
+
## 0.2.6
|
|
9
|
+
|
|
10
|
+
- “Save this list” is a full-width 44px control on the same 16px edge as the rest of the changelist.
|
|
11
|
+
- While the sticky bulk bar is open, the top action menu and Run are hidden. Select all and the count stay. With JavaScript off, that top row stays in place.
|
|
12
|
+
|
|
13
|
+
## 0.2.5
|
|
14
|
+
|
|
15
|
+
- A selected changelist card uses a light accent tint and outline instead of Django’s flat selected-row fill.
|
|
16
|
+
- The checkbox and the linked column share one left-aligned title line. Field labels stay on the rows below, about 40% wide, with values wrapping in the rest of the row.
|
|
17
|
+
- Rich cell HTML and list-editable inputs stay inside the value column. The sticky bulk bar is full width above the tab bar: the count, the action menu, and Run share one row.
|
|
18
|
+
|
|
3
19
|
## 0.2.4
|
|
4
20
|
|
|
5
21
|
- The action menu and Run share one 44px row and the same corner radius. Run stays only as wide as its label.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: django-admin-mobile
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Make the native Django admin usable on phones without replacing its look.
|
|
5
5
|
Project-URL: Homepage, https://github.com/daniilshamraev/django-admin-mobile
|
|
6
6
|
Project-URL: Repository, https://github.com/daniilshamraev/django-admin-mobile
|
|
@@ -40,15 +40,57 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
|
|
41
41
|
# django-admin-mobile
|
|
42
42
|
|
|
43
|
-
Phone
|
|
43
|
+
Phone ergonomics for the native Django admin. Same colors, same templates, a layout that fits a 375px screen.
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
[](https://pypi.org/project/django-admin-mobile/)
|
|
46
|
+
[](https://pypi.org/project/django-admin-mobile/)
|
|
47
|
+
[](https://docs.djangoproject.com/)
|
|
48
|
+
[](LICENSE)
|
|
49
|
+
[](https://github.com/daniilshamraev/django-admin-mobile/actions/workflows/ci.yml)
|
|
46
50
|
|
|
47
|
-

|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
## Before / after
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
Stock Django admin at 375px, then the same changelist with this package.
|
|
56
|
+
|
|
57
|
+
| Before | After |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/stock-changelist-375-dark.png"><img alt="Stock Django admin changelist at 375px" src="docs/screenshots/stock-changelist-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/changelist-375-dark.png"><img alt="Changelist with django-admin-mobile at 375px" src="docs/screenshots/changelist-375.png" width="250"></picture> |
|
|
60
|
+
|
|
61
|
+
## Changelist
|
|
62
|
+
|
|
63
|
+
Cards, a full-width action row, and a sticky bulk bar. Selecting a row hides the top action menu so Run appears once.
|
|
64
|
+
|
|
65
|
+
| Cards | One row selected | Bulk bar |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/changelist-375-dark.png"><img alt="Book changelist cards" src="docs/screenshots/changelist-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/payments-375-dark.png"><img alt="Payments changelist with nothing selected" src="docs/screenshots/payments-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/payments-selected-375-dark.png"><img alt="Payments changelist with the sticky bulk bar" src="docs/screenshots/payments-selected-375.png" width="250"></picture> |
|
|
68
|
+
|
|
69
|
+
## Filters
|
|
70
|
+
|
|
71
|
+
`FILTER_SHEET` opens filters in a bottom sheet: dimmed backdrop, grab handle, Reset, a close control, 44px rows, and Show results. Long lists get a search field. Date and range widgets stay inside the sheet. With JavaScript off, `#changelist-filter` stays inline.
|
|
72
|
+
|
|
73
|
+
| Light | Dark |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| <img alt="Filter bottom sheet, light" src="docs/screenshots/filters-375.png" width="250"> | <img alt="Filter bottom sheet, dark" src="docs/screenshots/filters-375-dark.png" width="250"> |
|
|
76
|
+
|
|
77
|
+
## Change form
|
|
78
|
+
|
|
79
|
+
Native date and time inputs, stacked inlines, and a submit bar that sits above the tab bar.
|
|
80
|
+
|
|
81
|
+
| Change form | Edition | History |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/change-form-375-dark.png"><img alt="Book change form" src="docs/screenshots/change-form-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/edition-375-dark.png"><img alt="Edition change form" src="docs/screenshots/edition-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/history-375-dark.png"><img alt="Object history" src="docs/screenshots/history-375.png" width="250"></picture> |
|
|
84
|
+
|
|
85
|
+
## Index and navigation
|
|
86
|
+
|
|
87
|
+
| Index | App index | Search |
|
|
88
|
+
| --- | --- | --- |
|
|
89
|
+
| <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/index-375-dark.png"><img alt="Admin index" src="docs/screenshots/index-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/app-index-375-dark.png"><img alt="App index" src="docs/screenshots/app-index-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/search-375-dark.png"><img alt="Global search" src="docs/screenshots/search-375.png" width="250"></picture> |
|
|
90
|
+
|
|
91
|
+
Screenshots are the 375px viewport, light and dark. More captures live in [`docs/screenshots/`](docs/screenshots/).
|
|
92
|
+
|
|
93
|
+
## Quick start
|
|
52
94
|
|
|
53
95
|
```bash
|
|
54
96
|
pip install django-admin-mobile
|
|
@@ -68,7 +110,7 @@ That is enough. `admin_mobile` overrides `admin/base.html`, calls `{{ block.supe
|
|
|
68
110
|
|
|
69
111
|
No CDN and no build step. The package ships plain files under `admin_mobile/static/`.
|
|
70
112
|
|
|
71
|
-
|
|
113
|
+
### If you already override `admin/base.html`
|
|
72
114
|
|
|
73
115
|
Django will use your template and skip this app's override. Load the tag yourself, and still call `{{ block.super }}`:
|
|
74
116
|
|
|
@@ -89,19 +131,14 @@ Django will use your template and skip this app's override. Load the tag yoursel
|
|
|
89
131
|
|
|
90
132
|
`{% admin_mobile_assets %}` emits both. If `admin_mobile` is listed after `django.contrib.admin`, system check `admin_mobile.W001` warns you.
|
|
91
133
|
|
|
92
|
-
##
|
|
134
|
+
## Configuration
|
|
93
135
|
|
|
94
|
-
|
|
136
|
+
Every key is optional. Unknown keys are rejected. Boolean flags must be real bools.
|
|
95
137
|
|
|
96
138
|
```python
|
|
97
139
|
ADMIN_MOBILE = {
|
|
98
140
|
"ENABLED": True,
|
|
99
|
-
"
|
|
100
|
-
"TABULAR_INLINE_STACK": True,
|
|
101
|
-
"STICKY_SUBMIT": True,
|
|
102
|
-
"TOUCH_TARGETS": True,
|
|
103
|
-
"COLLAPSIBLE_FILTERS": True,
|
|
104
|
-
"USE_NATIVE_DATETIME": False,
|
|
141
|
+
"FILTER_SHEET": True,
|
|
105
142
|
}
|
|
106
143
|
```
|
|
107
144
|
|
|
@@ -113,14 +150,14 @@ ADMIN_MOBILE = {
|
|
|
113
150
|
| `STICKY_SUBMIT` | `True` | The last `.submit-row` sticks to the bottom of the viewport. The login page is left alone. |
|
|
114
151
|
| `TOUCH_TARGETS` | `True` | Targets at least 44px, inputs at 16px (stops iOS from zooming), larger checkboxes. |
|
|
115
152
|
| `COLLAPSIBLE_FILTERS` | `True` | `#changelist-filter` collapses behind a button. Django's filters, django-admin-rangefilter, and django-more-admin-filters render inside that node; this package does not import them. |
|
|
153
|
+
| `FILTER_SHEET` | `True` | On a phone, filters open as a bottom sheet with Reset, a close control, an active-count badge, search on long lists, and Show results. With JavaScript off, the filter block stays inline. |
|
|
116
154
|
| `USE_NATIVE_DATETIME` | `False` | Opt in to native date/time widgets on **every** `ModelAdmin` and inline. A custom `formfield_overrides` widget is kept. |
|
|
117
155
|
| `BOTTOM_TABS` | `True` | Home, search, recent, and saved-model tabs. Favorites stay in `localStorage`. |
|
|
118
156
|
| `GLOBAL_SEARCH` | `True` | Staff search across admins that define `search_fields`. Permissions are checked per model. |
|
|
119
157
|
| `SWIPE_ACTIONS` | `True` | Swipe a card right to open it, left for Open/Delete. Delete still posts `delete_selected` after confirm. |
|
|
120
158
|
| `PULL_TO_REFRESH` | `True` | Pull down on a changelist to reload. |
|
|
121
159
|
| `INFINITE_SCROLL` | `False` | Fetch the next changelist page instead of using the pager. |
|
|
122
|
-
| `
|
|
123
|
-
| `STICKY_ACTIONS` | `True` | `Selected N`, the action menu, and Run stick above the tab bar. |
|
|
160
|
+
| `STICKY_ACTIONS` | `True` | `Selected N`, the action menu, and Run stick above the tab bar. While that bar is open, the top action menu is hidden. |
|
|
124
161
|
| `UNSAVED_GUARD` | `True` | `beforeunload` when a change form is dirty. |
|
|
125
162
|
| `FORM_DRAFTS` | `False` | Autosave add-form text fields in `localStorage` and offer restore. Passwords and files are skipped. |
|
|
126
163
|
| `CAMERA_CAPTURE` | `True` | Camera button on file inputs, with a preview. |
|
|
@@ -137,18 +174,16 @@ ADMIN_MOBILE = {
|
|
|
137
174
|
| `ACCENT` | unset | Hex color (`#rgb` or `#rrggbb`) mapped to `--button-bg`, `--default-button-bg`, and `--object-tools-bg` for both `data-theme` values and `prefers-color-scheme`. It does not set `--accent`, because that variable is also the header title color. |
|
|
138
175
|
| `PALETTE` | unset | Optional map of allow-listed admin variable names (`primary`, `header-bg`, `link-fg`, …) to hex colors. |
|
|
139
176
|
| `READONLY_MODELS` | `[]` | `app.model` labels that lose add/change/delete when the `admin_mobile_viewport=mobile` cookie is set. |
|
|
140
|
-
|
|
141
|
-
`
|
|
177
|
+
| `UNDO_SECONDS` | `8` | Seconds to wait when `UNDO_DELETE` is on. An int from 5 to 10. |
|
|
178
|
+
| `SWIPE_CONFIRM_ACTIONS` | `["delete_selected"]` | Action names that use the slide-to-confirm control. |
|
|
142
179
|
|
|
143
180
|
`mobile_readonly` and `READONLY_MODELS` only look at a cookie the phone script sets. That is a UX guard. It does not replace permissions: a desktop client, or a client that drops the cookie, keeps the real Django permissions.
|
|
144
181
|
|
|
145
182
|
Surfaces use Django admin CSS variables, including `html[data-theme]` and `prefers-color-scheme`. The service worker does not cache HTML or authenticated responses.
|
|
146
183
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
## Native date and time widgets
|
|
184
|
+
## MobileAdminMixin
|
|
150
185
|
|
|
151
|
-
Per model, without the global flag:
|
|
186
|
+
Per model, without the global date flag:
|
|
152
187
|
|
|
153
188
|
```python
|
|
154
189
|
from django.contrib import admin
|
|
@@ -158,7 +193,11 @@ from library.models import Book
|
|
|
158
193
|
|
|
159
194
|
@admin.register(Book)
|
|
160
195
|
class BookAdmin(MobileAdminMixin, admin.ModelAdmin):
|
|
161
|
-
|
|
196
|
+
mobile_card_title = "title"
|
|
197
|
+
mobile_card_subtitle = "author"
|
|
198
|
+
mobile_list_display = ("status", "published_on")
|
|
199
|
+
# mobile_card_image = "cover"
|
|
200
|
+
# mobile_readonly = True
|
|
162
201
|
```
|
|
163
202
|
|
|
164
203
|
`MobileAdminMixin` must come before `admin.ModelAdmin` or `admin.TabularInline`. It sets `formfield_overrides` to:
|
|
@@ -171,6 +210,12 @@ Values stay on `SplitDateTimeField` / `DateField` / `TimeField`, so Django still
|
|
|
171
210
|
|
|
172
211
|
These widgets do **not** load `DateTimeShortcuts`, so you do not get a second calendar on top of the native picker.
|
|
173
212
|
|
|
213
|
+
## Compatibility
|
|
214
|
+
|
|
215
|
+
Python 3.10+ and Django 4.2 LTS, 5.2, and 6.0+. Django 6 needs Python 3.12+. `tox.ini` and GitHub Actions run that matrix.
|
|
216
|
+
|
|
217
|
+
The stylesheet also covers the rest of the built-in admin at `≤600px`: login, password change, and logout; the index and app list; the nav sidebar and its filter; header, user tools, and breadcrumbs; changelist search, actions, select-all, sortable headers, date hierarchy, pagination, result counts, and `list_editable`; every built-in filter (choices, related, date, empty, and facets on Django 5+); change-form fieldsets (including collapse), help, errors, readonly fields, related-object icons, raw id lookup, autocomplete, filter horizontal/vertical, radios, and file Currently/Change/Clear; stacked and tabular inlines; object history; delete confirmation; related-object popups; messages; the stock calendar and clock when native widgets are off; admindocs; and RTL (`dir="rtl"`).
|
|
218
|
+
|
|
174
219
|
## Example
|
|
175
220
|
|
|
176
221
|
```bash
|
|
@@ -183,17 +228,11 @@ With `DEBUG` and `ADMIN_MOBILE_DEMO` (both set in `example/example/settings.py`)
|
|
|
183
228
|
|
|
184
229
|
Open http://127.0.0.1:8741/admin/ and narrow the window to 375px, or use device mode.
|
|
185
230
|
|
|
186
|
-

|
|
187
|
-
|
|
188
|
-

|
|
189
|
-
|
|
190
|
-
The same stylesheet also covers the rest of the built-in admin at `≤600px`: login, password change, and logout; the index and app list; the nav sidebar and its filter; header, user tools, and breadcrumbs; changelist search, actions, select-all, sortable headers, date hierarchy, pagination, result counts, and `list_editable`; every built-in filter (choices, related, date, empty, and facets on Django 5+); change-form fieldsets (including collapse), help, errors, readonly fields, related-object icons, raw id lookup, autocomplete, filter horizontal/vertical, radios, and file Currently/Change/Clear; stacked and tabular inlines; object history; delete confirmation; related-object popups; messages; the stock calendar and clock when native widgets are off; admindocs; and RTL (`dir="rtl"`, logical alignment).
|
|
191
|
-
|
|
192
|
-
Further 375px captures live in `docs/screenshots/` (`login`, `index`, `nav-sidebar`, `edition`, `calendar`, `clock`, `history`, `delete-confirm`, `popup`, `admindocs`, `rtl-changelist`, and others).
|
|
193
|
-
|
|
194
231
|
`Appointment` in the example keeps Django's calendar and clock. `Book` and `Edition` use `MobileAdminMixin`. `USE_NATIVE_DATETIME` stays off in the example so both modes are visible.
|
|
195
232
|
|
|
196
|
-
##
|
|
233
|
+
## Contributing
|
|
234
|
+
|
|
235
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
197
236
|
|
|
198
237
|
```bash
|
|
199
238
|
uv run ruff check .
|
|
@@ -203,14 +242,14 @@ uv run pytest -m playwright
|
|
|
203
242
|
uv run tox
|
|
204
243
|
```
|
|
205
244
|
|
|
206
|
-
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT. Copyright (c) 2026 Daniil Shamraev. See [LICENSE](LICENSE).
|
|
207
248
|
|
|
208
249
|
## Publishing
|
|
209
250
|
|
|
210
|
-
0.1.0 is already on PyPI. This tree is 0.2.
|
|
251
|
+
0.1.0 is already on PyPI. This tree is 0.2.7 and is not published from here.
|
|
211
252
|
|
|
212
|
-
1. Tag `0.2.
|
|
253
|
+
1. Tag `0.2.7` when you want a release.
|
|
213
254
|
2. Build: `uv build`.
|
|
214
255
|
3. Publish: `uv publish` with a PyPI token.
|
|
215
|
-
|
|
216
|
-
Author: Daniil Shamraev. License: MIT.
|
|
@@ -1,14 +1,56 @@
|
|
|
1
1
|
# django-admin-mobile
|
|
2
2
|
|
|
3
|
-
Phone
|
|
3
|
+
Phone ergonomics for the native Django admin. Same colors, same templates, a layout that fits a 375px screen.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://pypi.org/project/django-admin-mobile/)
|
|
6
|
+
[](https://pypi.org/project/django-admin-mobile/)
|
|
7
|
+
[](https://docs.djangoproject.com/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://github.com/daniilshamraev/django-admin-mobile/actions/workflows/ci.yml)
|
|
6
10
|
|
|
7
|
-

|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
## Before / after
|
|
10
14
|
|
|
11
|
-
|
|
15
|
+
Stock Django admin at 375px, then the same changelist with this package.
|
|
16
|
+
|
|
17
|
+
| Before | After |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/stock-changelist-375-dark.png"><img alt="Stock Django admin changelist at 375px" src="docs/screenshots/stock-changelist-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/changelist-375-dark.png"><img alt="Changelist with django-admin-mobile at 375px" src="docs/screenshots/changelist-375.png" width="250"></picture> |
|
|
20
|
+
|
|
21
|
+
## Changelist
|
|
22
|
+
|
|
23
|
+
Cards, a full-width action row, and a sticky bulk bar. Selecting a row hides the top action menu so Run appears once.
|
|
24
|
+
|
|
25
|
+
| Cards | One row selected | Bulk bar |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/changelist-375-dark.png"><img alt="Book changelist cards" src="docs/screenshots/changelist-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/payments-375-dark.png"><img alt="Payments changelist with nothing selected" src="docs/screenshots/payments-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/payments-selected-375-dark.png"><img alt="Payments changelist with the sticky bulk bar" src="docs/screenshots/payments-selected-375.png" width="250"></picture> |
|
|
28
|
+
|
|
29
|
+
## Filters
|
|
30
|
+
|
|
31
|
+
`FILTER_SHEET` opens filters in a bottom sheet: dimmed backdrop, grab handle, Reset, a close control, 44px rows, and Show results. Long lists get a search field. Date and range widgets stay inside the sheet. With JavaScript off, `#changelist-filter` stays inline.
|
|
32
|
+
|
|
33
|
+
| Light | Dark |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| <img alt="Filter bottom sheet, light" src="docs/screenshots/filters-375.png" width="250"> | <img alt="Filter bottom sheet, dark" src="docs/screenshots/filters-375-dark.png" width="250"> |
|
|
36
|
+
|
|
37
|
+
## Change form
|
|
38
|
+
|
|
39
|
+
Native date and time inputs, stacked inlines, and a submit bar that sits above the tab bar.
|
|
40
|
+
|
|
41
|
+
| Change form | Edition | History |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/change-form-375-dark.png"><img alt="Book change form" src="docs/screenshots/change-form-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/edition-375-dark.png"><img alt="Edition change form" src="docs/screenshots/edition-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/history-375-dark.png"><img alt="Object history" src="docs/screenshots/history-375.png" width="250"></picture> |
|
|
44
|
+
|
|
45
|
+
## Index and navigation
|
|
46
|
+
|
|
47
|
+
| Index | App index | Search |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/index-375-dark.png"><img alt="Admin index" src="docs/screenshots/index-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/app-index-375-dark.png"><img alt="App index" src="docs/screenshots/app-index-375.png" width="250"></picture> | <picture><source media="(prefers-color-scheme: dark)" srcset="docs/screenshots/search-375-dark.png"><img alt="Global search" src="docs/screenshots/search-375.png" width="250"></picture> |
|
|
50
|
+
|
|
51
|
+
Screenshots are the 375px viewport, light and dark. More captures live in [`docs/screenshots/`](docs/screenshots/).
|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
12
54
|
|
|
13
55
|
```bash
|
|
14
56
|
pip install django-admin-mobile
|
|
@@ -28,7 +70,7 @@ That is enough. `admin_mobile` overrides `admin/base.html`, calls `{{ block.supe
|
|
|
28
70
|
|
|
29
71
|
No CDN and no build step. The package ships plain files under `admin_mobile/static/`.
|
|
30
72
|
|
|
31
|
-
|
|
73
|
+
### If you already override `admin/base.html`
|
|
32
74
|
|
|
33
75
|
Django will use your template and skip this app's override. Load the tag yourself, and still call `{{ block.super }}`:
|
|
34
76
|
|
|
@@ -49,19 +91,14 @@ Django will use your template and skip this app's override. Load the tag yoursel
|
|
|
49
91
|
|
|
50
92
|
`{% admin_mobile_assets %}` emits both. If `admin_mobile` is listed after `django.contrib.admin`, system check `admin_mobile.W001` warns you.
|
|
51
93
|
|
|
52
|
-
##
|
|
94
|
+
## Configuration
|
|
53
95
|
|
|
54
|
-
|
|
96
|
+
Every key is optional. Unknown keys are rejected. Boolean flags must be real bools.
|
|
55
97
|
|
|
56
98
|
```python
|
|
57
99
|
ADMIN_MOBILE = {
|
|
58
100
|
"ENABLED": True,
|
|
59
|
-
"
|
|
60
|
-
"TABULAR_INLINE_STACK": True,
|
|
61
|
-
"STICKY_SUBMIT": True,
|
|
62
|
-
"TOUCH_TARGETS": True,
|
|
63
|
-
"COLLAPSIBLE_FILTERS": True,
|
|
64
|
-
"USE_NATIVE_DATETIME": False,
|
|
101
|
+
"FILTER_SHEET": True,
|
|
65
102
|
}
|
|
66
103
|
```
|
|
67
104
|
|
|
@@ -73,14 +110,14 @@ ADMIN_MOBILE = {
|
|
|
73
110
|
| `STICKY_SUBMIT` | `True` | The last `.submit-row` sticks to the bottom of the viewport. The login page is left alone. |
|
|
74
111
|
| `TOUCH_TARGETS` | `True` | Targets at least 44px, inputs at 16px (stops iOS from zooming), larger checkboxes. |
|
|
75
112
|
| `COLLAPSIBLE_FILTERS` | `True` | `#changelist-filter` collapses behind a button. Django's filters, django-admin-rangefilter, and django-more-admin-filters render inside that node; this package does not import them. |
|
|
113
|
+
| `FILTER_SHEET` | `True` | On a phone, filters open as a bottom sheet with Reset, a close control, an active-count badge, search on long lists, and Show results. With JavaScript off, the filter block stays inline. |
|
|
76
114
|
| `USE_NATIVE_DATETIME` | `False` | Opt in to native date/time widgets on **every** `ModelAdmin` and inline. A custom `formfield_overrides` widget is kept. |
|
|
77
115
|
| `BOTTOM_TABS` | `True` | Home, search, recent, and saved-model tabs. Favorites stay in `localStorage`. |
|
|
78
116
|
| `GLOBAL_SEARCH` | `True` | Staff search across admins that define `search_fields`. Permissions are checked per model. |
|
|
79
117
|
| `SWIPE_ACTIONS` | `True` | Swipe a card right to open it, left for Open/Delete. Delete still posts `delete_selected` after confirm. |
|
|
80
118
|
| `PULL_TO_REFRESH` | `True` | Pull down on a changelist to reload. |
|
|
81
119
|
| `INFINITE_SCROLL` | `False` | Fetch the next changelist page instead of using the pager. |
|
|
82
|
-
| `
|
|
83
|
-
| `STICKY_ACTIONS` | `True` | `Selected N`, the action menu, and Run stick above the tab bar. |
|
|
120
|
+
| `STICKY_ACTIONS` | `True` | `Selected N`, the action menu, and Run stick above the tab bar. While that bar is open, the top action menu is hidden. |
|
|
84
121
|
| `UNSAVED_GUARD` | `True` | `beforeunload` when a change form is dirty. |
|
|
85
122
|
| `FORM_DRAFTS` | `False` | Autosave add-form text fields in `localStorage` and offer restore. Passwords and files are skipped. |
|
|
86
123
|
| `CAMERA_CAPTURE` | `True` | Camera button on file inputs, with a preview. |
|
|
@@ -97,18 +134,16 @@ ADMIN_MOBILE = {
|
|
|
97
134
|
| `ACCENT` | unset | Hex color (`#rgb` or `#rrggbb`) mapped to `--button-bg`, `--default-button-bg`, and `--object-tools-bg` for both `data-theme` values and `prefers-color-scheme`. It does not set `--accent`, because that variable is also the header title color. |
|
|
98
135
|
| `PALETTE` | unset | Optional map of allow-listed admin variable names (`primary`, `header-bg`, `link-fg`, …) to hex colors. |
|
|
99
136
|
| `READONLY_MODELS` | `[]` | `app.model` labels that lose add/change/delete when the `admin_mobile_viewport=mobile` cookie is set. |
|
|
100
|
-
|
|
101
|
-
`
|
|
137
|
+
| `UNDO_SECONDS` | `8` | Seconds to wait when `UNDO_DELETE` is on. An int from 5 to 10. |
|
|
138
|
+
| `SWIPE_CONFIRM_ACTIONS` | `["delete_selected"]` | Action names that use the slide-to-confirm control. |
|
|
102
139
|
|
|
103
140
|
`mobile_readonly` and `READONLY_MODELS` only look at a cookie the phone script sets. That is a UX guard. It does not replace permissions: a desktop client, or a client that drops the cookie, keeps the real Django permissions.
|
|
104
141
|
|
|
105
142
|
Surfaces use Django admin CSS variables, including `html[data-theme]` and `prefers-color-scheme`. The service worker does not cache HTML or authenticated responses.
|
|
106
143
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
## Native date and time widgets
|
|
144
|
+
## MobileAdminMixin
|
|
110
145
|
|
|
111
|
-
Per model, without the global flag:
|
|
146
|
+
Per model, without the global date flag:
|
|
112
147
|
|
|
113
148
|
```python
|
|
114
149
|
from django.contrib import admin
|
|
@@ -118,7 +153,11 @@ from library.models import Book
|
|
|
118
153
|
|
|
119
154
|
@admin.register(Book)
|
|
120
155
|
class BookAdmin(MobileAdminMixin, admin.ModelAdmin):
|
|
121
|
-
|
|
156
|
+
mobile_card_title = "title"
|
|
157
|
+
mobile_card_subtitle = "author"
|
|
158
|
+
mobile_list_display = ("status", "published_on")
|
|
159
|
+
# mobile_card_image = "cover"
|
|
160
|
+
# mobile_readonly = True
|
|
122
161
|
```
|
|
123
162
|
|
|
124
163
|
`MobileAdminMixin` must come before `admin.ModelAdmin` or `admin.TabularInline`. It sets `formfield_overrides` to:
|
|
@@ -131,6 +170,12 @@ Values stay on `SplitDateTimeField` / `DateField` / `TimeField`, so Django still
|
|
|
131
170
|
|
|
132
171
|
These widgets do **not** load `DateTimeShortcuts`, so you do not get a second calendar on top of the native picker.
|
|
133
172
|
|
|
173
|
+
## Compatibility
|
|
174
|
+
|
|
175
|
+
Python 3.10+ and Django 4.2 LTS, 5.2, and 6.0+. Django 6 needs Python 3.12+. `tox.ini` and GitHub Actions run that matrix.
|
|
176
|
+
|
|
177
|
+
The stylesheet also covers the rest of the built-in admin at `≤600px`: login, password change, and logout; the index and app list; the nav sidebar and its filter; header, user tools, and breadcrumbs; changelist search, actions, select-all, sortable headers, date hierarchy, pagination, result counts, and `list_editable`; every built-in filter (choices, related, date, empty, and facets on Django 5+); change-form fieldsets (including collapse), help, errors, readonly fields, related-object icons, raw id lookup, autocomplete, filter horizontal/vertical, radios, and file Currently/Change/Clear; stacked and tabular inlines; object history; delete confirmation; related-object popups; messages; the stock calendar and clock when native widgets are off; admindocs; and RTL (`dir="rtl"`).
|
|
178
|
+
|
|
134
179
|
## Example
|
|
135
180
|
|
|
136
181
|
```bash
|
|
@@ -143,17 +188,11 @@ With `DEBUG` and `ADMIN_MOBILE_DEMO` (both set in `example/example/settings.py`)
|
|
|
143
188
|
|
|
144
189
|
Open http://127.0.0.1:8741/admin/ and narrow the window to 375px, or use device mode.
|
|
145
190
|
|
|
146
|
-

|
|
147
|
-
|
|
148
|
-

|
|
149
|
-
|
|
150
|
-
The same stylesheet also covers the rest of the built-in admin at `≤600px`: login, password change, and logout; the index and app list; the nav sidebar and its filter; header, user tools, and breadcrumbs; changelist search, actions, select-all, sortable headers, date hierarchy, pagination, result counts, and `list_editable`; every built-in filter (choices, related, date, empty, and facets on Django 5+); change-form fieldsets (including collapse), help, errors, readonly fields, related-object icons, raw id lookup, autocomplete, filter horizontal/vertical, radios, and file Currently/Change/Clear; stacked and tabular inlines; object history; delete confirmation; related-object popups; messages; the stock calendar and clock when native widgets are off; admindocs; and RTL (`dir="rtl"`, logical alignment).
|
|
151
|
-
|
|
152
|
-
Further 375px captures live in `docs/screenshots/` (`login`, `index`, `nav-sidebar`, `edition`, `calendar`, `clock`, `history`, `delete-confirm`, `popup`, `admindocs`, `rtl-changelist`, and others).
|
|
153
|
-
|
|
154
191
|
`Appointment` in the example keeps Django's calendar and clock. `Book` and `Edition` use `MobileAdminMixin`. `USE_NATIVE_DATETIME` stays off in the example so both modes are visible.
|
|
155
192
|
|
|
156
|
-
##
|
|
193
|
+
## Contributing
|
|
194
|
+
|
|
195
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
157
196
|
|
|
158
197
|
```bash
|
|
159
198
|
uv run ruff check .
|
|
@@ -163,14 +202,14 @@ uv run pytest -m playwright
|
|
|
163
202
|
uv run tox
|
|
164
203
|
```
|
|
165
204
|
|
|
166
|
-
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
MIT. Copyright (c) 2026 Daniil Shamraev. See [LICENSE](LICENSE).
|
|
167
208
|
|
|
168
209
|
## Publishing
|
|
169
210
|
|
|
170
|
-
0.1.0 is already on PyPI. This tree is 0.2.
|
|
211
|
+
0.1.0 is already on PyPI. This tree is 0.2.7 and is not published from here.
|
|
171
212
|
|
|
172
|
-
1. Tag `0.2.
|
|
213
|
+
1. Tag `0.2.7` when you want a release.
|
|
173
214
|
2. Build: `uv build`.
|
|
174
215
|
3. Publish: `uv publish` with a PyPI token.
|
|
175
|
-
|
|
176
|
-
Author: Daniil Shamraev. License: MIT.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from django.contrib import admin
|
|
4
|
+
from django.templatetags.static import static
|
|
5
|
+
from django.utils.html import format_html
|
|
4
6
|
|
|
5
7
|
from admin_mobile.mixins import MobileAdminMixin
|
|
6
8
|
from library.models import (
|
|
@@ -10,6 +12,7 @@ from library.models import (
|
|
|
10
12
|
Chapter,
|
|
11
13
|
Citation,
|
|
12
14
|
Edition,
|
|
15
|
+
Payment,
|
|
13
16
|
Publisher,
|
|
14
17
|
Revision,
|
|
15
18
|
Series,
|
|
@@ -142,6 +145,46 @@ class RevisionAdmin(admin.ModelAdmin):
|
|
|
142
145
|
search_fields = ("label",)
|
|
143
146
|
|
|
144
147
|
|
|
148
|
+
@admin.action(description="Отметить проверенным")
|
|
149
|
+
def mark_reviewed(
|
|
150
|
+
modeladmin: admin.ModelAdmin, request: object, queryset: object
|
|
151
|
+
) -> None:
|
|
152
|
+
del modeladmin, request
|
|
153
|
+
queryset.update(reviewed=True)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@admin.register(Payment)
|
|
157
|
+
class PaymentAdmin(MobileAdminMixin, admin.ModelAdmin):
|
|
158
|
+
"""No card-title hint: the link column itself has to become the title."""
|
|
159
|
+
|
|
160
|
+
list_display = (
|
|
161
|
+
"id",
|
|
162
|
+
"payer",
|
|
163
|
+
"amount",
|
|
164
|
+
"system",
|
|
165
|
+
"status",
|
|
166
|
+
"paid_on",
|
|
167
|
+
"external_id",
|
|
168
|
+
"comment",
|
|
169
|
+
"currency",
|
|
170
|
+
)
|
|
171
|
+
list_display_links = ("id",)
|
|
172
|
+
list_editable = ("amount",)
|
|
173
|
+
list_per_page = 10
|
|
174
|
+
actions = (mark_reviewed,)
|
|
175
|
+
|
|
176
|
+
@admin.display(description="Пользователь")
|
|
177
|
+
def payer(self, obj: Payment) -> str:
|
|
178
|
+
return format_html(
|
|
179
|
+
'<img alt="" src="{}" width="16" height="16"> <strong>{}</strong> '
|
|
180
|
+
'(<a href="https://vk.com/id{}">vk.com/id{}</a>)',
|
|
181
|
+
static("admin/img/icon-viewlink.svg"),
|
|
182
|
+
obj.payer_name,
|
|
183
|
+
obj.vk_id,
|
|
184
|
+
obj.vk_id,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
145
188
|
@admin.register(Appointment)
|
|
146
189
|
class AppointmentAdmin(admin.ModelAdmin):
|
|
147
190
|
"""Stock widgets on purpose, so the admin calendar and clock remain."""
|
|
@@ -26,6 +26,7 @@ def seed_demo(*args: object, **kwargs: object) -> bool:
|
|
|
26
26
|
Chapter,
|
|
27
27
|
Citation,
|
|
28
28
|
Edition,
|
|
29
|
+
Payment,
|
|
29
30
|
Publisher,
|
|
30
31
|
Revision,
|
|
31
32
|
Series,
|
|
@@ -140,4 +141,15 @@ def seed_demo(*args: object, **kwargs: object) -> bool:
|
|
|
140
141
|
at_time=time(18, 30),
|
|
141
142
|
when=timezone.make_aware(datetime(2026, 4, 2, 18, 30)),
|
|
142
143
|
)
|
|
144
|
+
Payment.objects.create(
|
|
145
|
+
payer_name="Мария Александровна Волконская",
|
|
146
|
+
vk_id="123456789012345678",
|
|
147
|
+
amount="1490.50",
|
|
148
|
+
system=Payment.System.WALLET,
|
|
149
|
+
status="Оплачен",
|
|
150
|
+
paid_on=date(2026, 3, 2),
|
|
151
|
+
external_id="pay-1843-notes",
|
|
152
|
+
comment="Перевод за второе издание",
|
|
153
|
+
currency="RUB",
|
|
154
|
+
)
|
|
143
155
|
return True
|