django-admin-rest-api 0.1.0a0__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 (39) hide show
  1. django_admin_rest_api-0.1.0a0/LICENSE +21 -0
  2. django_admin_rest_api-0.1.0a0/PKG-INFO +251 -0
  3. django_admin_rest_api-0.1.0a0/README.md +217 -0
  4. django_admin_rest_api-0.1.0a0/django_admin_rest_api/__init__.py +18 -0
  5. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/README.md +31 -0
  6. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/__init__.py +5 -0
  7. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/dates.py +215 -0
  8. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/filters.py +412 -0
  9. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/inlines.py +397 -0
  10. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/inlines_write.py +241 -0
  11. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/panels.py +113 -0
  12. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/permissions.py +132 -0
  13. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/registry.py +399 -0
  14. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/serializers.py +467 -0
  15. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/urls.py +170 -0
  16. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/README.md +22 -0
  17. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/__init__.py +6 -0
  18. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/actions.py +196 -0
  19. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/auth.py +185 -0
  20. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/autocomplete.py +163 -0
  21. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/bulk.py +248 -0
  22. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/create.py +214 -0
  23. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/create_form.py +229 -0
  24. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/delete_preview.py +107 -0
  25. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/destroy.py +93 -0
  26. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/detail.py +499 -0
  27. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/history.py +166 -0
  28. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/list.py +493 -0
  29. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/password.py +161 -0
  30. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/recent_actions.py +139 -0
  31. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/registry.py +53 -0
  32. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/schema.py +494 -0
  33. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/views/update.py +220 -0
  34. django_admin_rest_api-0.1.0a0/django_admin_rest_api/api/writes.py +541 -0
  35. django_admin_rest_api-0.1.0a0/django_admin_rest_api/apps.py +27 -0
  36. django_admin_rest_api-0.1.0a0/django_admin_rest_api/audit.py +58 -0
  37. django_admin_rest_api-0.1.0a0/django_admin_rest_api/conf.py +79 -0
  38. django_admin_rest_api-0.1.0a0/django_admin_rest_api/urls.py +28 -0
  39. django_admin_rest_api-0.1.0a0/pyproject.toml +212 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Martín Castro Álvarez and django-admin-rest-api contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,251 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-admin-rest-api
3
+ Version: 0.1.0a0
4
+ Summary: A JSON REST API for the Django admin — same permissions, same ModelAdmin, no new features. Powers django-admin-react and django-admin-mcp.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: django,admin,rest,api,json,headless
8
+ Author: django-admin-rest-api contributors
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Web Environment
12
+ Classifier: Framework :: Django
13
+ Classifier: Framework :: Django :: 5.0
14
+ Classifier: Framework :: Django :: 5.1
15
+ Classifier: Framework :: Django :: 5.2
16
+ Classifier: Framework :: Django :: 6.0
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
26
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
27
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
28
+ Requires-Dist: django (>=5.0,<7.0)
29
+ Project-URL: Documentation, https://github.com/MartinCastroAlvarez/django-admin-api#readme
30
+ Project-URL: Homepage, https://github.com/MartinCastroAlvarez/django-admin-api
31
+ Project-URL: Repository, https://github.com/MartinCastroAlvarez/django-admin-api
32
+ Description-Content-Type: text/markdown
33
+
34
+ # django-admin-rest-api
35
+
36
+ > A JSON REST API for the Django admin — **same permissions, same `ModelAdmin`, no new features.**
37
+
38
+ [![PyPI version](https://img.shields.io/pypi/v/django-admin-rest-api.svg)](https://pypi.org/project/django-admin-rest-api/)
39
+ [![Python versions](https://img.shields.io/pypi/pyversions/django-admin-rest-api.svg)](https://pypi.org/project/django-admin-rest-api/)
40
+ [![Django versions](https://img.shields.io/badge/Django-5.0%20%7C%205.1%20%7C%205.2%20%7C%206.0-44b78b.svg)](https://www.djangoproject.com/)
41
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
42
+
43
+ `django-admin-rest-api` exposes every `ModelAdmin` you've already
44
+ registered on `django.contrib.admin.site` (or your own `AdminSite`)
45
+ through a JSON REST API — **without** introducing a parallel
46
+ permission system, a parallel form layer, or any features the Django
47
+ admin itself doesn't have.
48
+
49
+ It is the wire surface that lets these projects drive your admin:
50
+
51
+ | Project | Role | PyPI |
52
+ | --- | --- | --- |
53
+ | 🟦 [`django-admin-react`](https://github.com/MartinCastroAlvarez/django-admin-react) | React single-page admin frontend | [`django-admin-react`](https://pypi.org/project/django-admin-react/) |
54
+ | 🟩 **`django-admin-rest-api`** *(this repo)* | JSON REST API over `ModelAdmin` | [`django-admin-rest-api`](https://pypi.org/project/django-admin-rest-api/) |
55
+ | 🟪 [`django-admin-mcp`](https://github.com/MartinCastroAlvarez/django-admin-mcp) | MCP server exposing the same API to LLMs | *(coming soon)* |
56
+
57
+ ---
58
+
59
+ ## ✨ The one design principle
60
+
61
+ **This package adds no new behavior. It is a JSON wrapper.**
62
+
63
+ That means every one of these is owned by your existing Django setup —
64
+ not by this library:
65
+
66
+ - 🔐 **Authentication** — Django's session + login. The API enforces the
67
+ same `is_active` + `is_staff` + `AdminSite.has_permission` gate the
68
+ HTML admin uses. No tokens, no custom auth backends, no JWTs.
69
+ - 🛡️ **Authorization / permissions** — every endpoint calls the
70
+ matching `ModelAdmin.has_view_permission` / `has_add_permission` /
71
+ `has_change_permission` / `has_delete_permission`. If your admin
72
+ says no, the API says 403.
73
+ - 📋 **Field validation** — `POST` / `PATCH` route the payload through
74
+ the same `ModelForm` Django would render in the HTML admin
75
+ (`ModelAdmin.get_form(request, obj)`), so every clean method, every
76
+ `unique_together` constraint, every custom widget validator runs
77
+ exactly once and exactly the same way.
78
+ - ⚙️ **Actions** — the action registry comes from
79
+ `ModelAdmin.get_actions(request)`. Your custom action functions run
80
+ unmodified.
81
+ - 🔎 **Search & filters** — search uses
82
+ `ModelAdmin.get_search_results(request, queryset, term)`; filters
83
+ use `ModelAdmin.list_filter`. No parallel implementation.
84
+ - 📜 **Audit log** — writes go through Django's `LogEntry` so your
85
+ history page (and every other consumer of `LogEntry`) keeps working.
86
+ - 🌐 **CSRF & sessions** — Django's middleware. Nothing is
87
+ `@csrf_exempt`.
88
+
89
+ If a behavior isn't in the HTML admin, it isn't here. If it is in the
90
+ HTML admin, this library exposes it over JSON.
91
+
92
+ ---
93
+
94
+ ## 🚀 Plug-and-play install
95
+
96
+ ```bash
97
+ pip install django-admin-rest-api
98
+ ```
99
+
100
+ Two changes to your project:
101
+
102
+ ```python
103
+ # settings.py
104
+ INSTALLED_APPS = [
105
+ # ... your existing apps ...
106
+ "django.contrib.admin",
107
+ "django_admin_rest_api", # ← add
108
+ ]
109
+ ```
110
+
111
+ ```python
112
+ # urls.py
113
+ from django.contrib import admin
114
+ from django.urls import include, path
115
+
116
+ urlpatterns = [
117
+ path("admin/", admin.site.urls),
118
+ path("admin-api/", include("django_admin_rest_api.urls")), # ← add
119
+ ]
120
+ ```
121
+
122
+ That's it. Your admin is now also a JSON API at `/admin-api/api/v1/...`.
123
+
124
+ ---
125
+
126
+ ## 📡 The endpoints
127
+
128
+ | Method | Path | What it returns |
129
+ | ------- | ---------------------------------------------- | -------------------------------------------------------- |
130
+ | `GET` | `/api/v1/registry/` | The same app/model tree Django renders in the admin index |
131
+ | `GET` | `/api/v1/schema/` | OpenAPI 3.1 schema of every endpoint below |
132
+ | `GET` | `/api/v1/<app>/<model>/` | List + pagination + filters + search |
133
+ | `POST` | `/api/v1/<app>/<model>/` | Create (runs the same `ModelForm`) |
134
+ | `GET` | `/api/v1/<app>/<model>/<pk>/` | Detail (read view as the HTML admin renders it) |
135
+ | `PATCH` | `/api/v1/<app>/<model>/<pk>/` | Update |
136
+ | `DELETE`| `/api/v1/<app>/<model>/<pk>/` | Destroy (with `LogEntry`) |
137
+ | `POST` | `/api/v1/<app>/<model>/bulk-update/` | Bulk patch |
138
+ | `POST` | `/api/v1/<app>/<model>/delete-preview/` | Cascade preview (like the HTML admin's confirm page) |
139
+ | `GET` | `/api/v1/<app>/<model>/autocomplete/?q=…` | `ModelAdmin.autocomplete_fields` source |
140
+ | `POST` | `/api/v1/<app>/<model>/actions/` | Run a `ModelAdmin` action on a selection |
141
+ | `GET` | `/api/v1/<app>/<model>/<pk>/history/` | The `LogEntry` history for one object |
142
+ | `GET` | `/api/v1/recent-actions/` | The dashboard's "Recent Actions" feed |
143
+ | `POST` | `/api/v1/login/` | Same `authenticate` + `login` as the HTML admin |
144
+ | `POST` | `/api/v1/logout/` | Same `logout` |
145
+ | `POST` | `/api/v1/<app>/<model>/<pk>/set-password/` | Auth-app `set_password` permission gate (User model) |
146
+
147
+ Every endpoint enforces the same permission gates as the HTML admin.
148
+
149
+ ---
150
+
151
+ ## 📸 Screenshots
152
+
153
+ The JSON `registry` endpoint — the source-of-truth for any consumer
154
+ frontend:
155
+
156
+ ![Registry endpoint JSON response](docs/screenshots/06-registry-api-json.png)
157
+
158
+ And here is the same admin rendered by
159
+ [`django-admin-react`](https://github.com/MartinCastroAlvarez/django-admin-react)
160
+ on top of this API, to give you an idea of what a consumer can build:
161
+
162
+ | | |
163
+ |:-:|:-:|
164
+ | ![SPA login](docs/screenshots/01-spa-login.png) | ![SPA registry](docs/screenshots/02-spa-registry.png) |
165
+ | ![SPA list](docs/screenshots/03-spa-list.png) | ![SPA detail](docs/screenshots/05-spa-detail.png) |
166
+
167
+ ---
168
+
169
+ ## ⚙️ Configuration
170
+
171
+ All settings live under a single optional dict — defaults are sane,
172
+ so most projects need no entry at all.
173
+
174
+ ```python
175
+ # settings.py (all keys optional)
176
+ DJANGO_ADMIN_REST_API = {
177
+ # Dotted path to the AdminSite whose ModelAdmin registry the API
178
+ # mirrors. Default exposes django.contrib.admin.site.
179
+ "ADMIN_SITE": "django.contrib.admin.site",
180
+
181
+ # Pagination. List endpoints use ModelAdmin.list_per_page as the
182
+ # source of truth; DEFAULT_PAGE_SIZE is the fallback. MAX_PAGE_SIZE
183
+ # caps ?page_size from the client (DoS guard).
184
+ "DEFAULT_PAGE_SIZE": 25,
185
+ "MAX_PAGE_SIZE": 200,
186
+
187
+ # When True, list responses include per-query timing in a debug
188
+ # block. Off by default — only enable in development.
189
+ "ENABLE_PROFILING": False,
190
+ }
191
+ ```
192
+
193
+ ---
194
+
195
+ ## 🔒 Security
196
+
197
+ - The API is **not** a parallel auth surface. It refuses any caller
198
+ the HTML admin would refuse, with the same gate
199
+ (`AdminSite.has_permission`, plus the per-model `ModelAdmin.has_*_permission`).
200
+ - Anonymous → `403` for every data endpoint.
201
+ - Authenticated but non-staff → `403`. Cookie present but resolved
202
+ user is anonymous → `403 not_authenticated`.
203
+ - Writes always go through `ModelForm.is_valid()` —
204
+ `unique_together`, `clean()`, field validators all run.
205
+ - Per-object guards run **before** the form does anything. The
206
+ `delete-preview` and `delete` endpoints both check `has_delete_permission(obj)`.
207
+ - CSRF is enforced everywhere. No view in this package is
208
+ `@csrf_exempt`. The login endpoint requires the CSRF cookie set
209
+ by the consumer's shell.
210
+
211
+ See the upstream
212
+ [`django-admin-react` SECURITY.md](https://github.com/MartinCastroAlvarez/django-admin-react/blob/main/SECURITY.md)
213
+ for the full threat model — the API surface is identical and the
214
+ guarantees transfer 1:1.
215
+
216
+ ---
217
+
218
+ ## 🧪 Local development
219
+
220
+ ```bash
221
+ git clone https://github.com/MartinCastroAlvarez/django-admin-api
222
+ cd django-admin-api
223
+ poetry install
224
+ poetry run pytest
225
+ poetry run ruff check .
226
+ poetry run black --check .
227
+ poetry run mypy django_admin_rest_api
228
+ poetry run bandit -c pyproject.toml -r django_admin_rest_api
229
+ ```
230
+
231
+ The test suite uses `pytest-django` + an in-memory SQLite database, so
232
+ no setup beyond `poetry install`.
233
+
234
+ ---
235
+
236
+ ## 🤝 Contributing
237
+
238
+ Issues, PRs, and Discussions are welcome on GitHub:
239
+ <https://github.com/MartinCastroAlvarez/django-admin-api>.
240
+
241
+ The lint + security gate is the same set the upstream
242
+ `django-admin-react` repo uses: **ruff, black, isort, flake8,
243
+ pylint, mypy, bandit, pip-audit, gitleaks.** Every change must pass
244
+ all of them before merge.
245
+
246
+ ---
247
+
248
+ ## 📜 License
249
+
250
+ MIT. See [LICENSE](LICENSE).
251
+
@@ -0,0 +1,217 @@
1
+ # django-admin-rest-api
2
+
3
+ > A JSON REST API for the Django admin — **same permissions, same `ModelAdmin`, no new features.**
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/django-admin-rest-api.svg)](https://pypi.org/project/django-admin-rest-api/)
6
+ [![Python versions](https://img.shields.io/pypi/pyversions/django-admin-rest-api.svg)](https://pypi.org/project/django-admin-rest-api/)
7
+ [![Django versions](https://img.shields.io/badge/Django-5.0%20%7C%205.1%20%7C%205.2%20%7C%206.0-44b78b.svg)](https://www.djangoproject.com/)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+
10
+ `django-admin-rest-api` exposes every `ModelAdmin` you've already
11
+ registered on `django.contrib.admin.site` (or your own `AdminSite`)
12
+ through a JSON REST API — **without** introducing a parallel
13
+ permission system, a parallel form layer, or any features the Django
14
+ admin itself doesn't have.
15
+
16
+ It is the wire surface that lets these projects drive your admin:
17
+
18
+ | Project | Role | PyPI |
19
+ | --- | --- | --- |
20
+ | 🟦 [`django-admin-react`](https://github.com/MartinCastroAlvarez/django-admin-react) | React single-page admin frontend | [`django-admin-react`](https://pypi.org/project/django-admin-react/) |
21
+ | 🟩 **`django-admin-rest-api`** *(this repo)* | JSON REST API over `ModelAdmin` | [`django-admin-rest-api`](https://pypi.org/project/django-admin-rest-api/) |
22
+ | 🟪 [`django-admin-mcp`](https://github.com/MartinCastroAlvarez/django-admin-mcp) | MCP server exposing the same API to LLMs | *(coming soon)* |
23
+
24
+ ---
25
+
26
+ ## ✨ The one design principle
27
+
28
+ **This package adds no new behavior. It is a JSON wrapper.**
29
+
30
+ That means every one of these is owned by your existing Django setup —
31
+ not by this library:
32
+
33
+ - 🔐 **Authentication** — Django's session + login. The API enforces the
34
+ same `is_active` + `is_staff` + `AdminSite.has_permission` gate the
35
+ HTML admin uses. No tokens, no custom auth backends, no JWTs.
36
+ - 🛡️ **Authorization / permissions** — every endpoint calls the
37
+ matching `ModelAdmin.has_view_permission` / `has_add_permission` /
38
+ `has_change_permission` / `has_delete_permission`. If your admin
39
+ says no, the API says 403.
40
+ - 📋 **Field validation** — `POST` / `PATCH` route the payload through
41
+ the same `ModelForm` Django would render in the HTML admin
42
+ (`ModelAdmin.get_form(request, obj)`), so every clean method, every
43
+ `unique_together` constraint, every custom widget validator runs
44
+ exactly once and exactly the same way.
45
+ - ⚙️ **Actions** — the action registry comes from
46
+ `ModelAdmin.get_actions(request)`. Your custom action functions run
47
+ unmodified.
48
+ - 🔎 **Search & filters** — search uses
49
+ `ModelAdmin.get_search_results(request, queryset, term)`; filters
50
+ use `ModelAdmin.list_filter`. No parallel implementation.
51
+ - 📜 **Audit log** — writes go through Django's `LogEntry` so your
52
+ history page (and every other consumer of `LogEntry`) keeps working.
53
+ - 🌐 **CSRF & sessions** — Django's middleware. Nothing is
54
+ `@csrf_exempt`.
55
+
56
+ If a behavior isn't in the HTML admin, it isn't here. If it is in the
57
+ HTML admin, this library exposes it over JSON.
58
+
59
+ ---
60
+
61
+ ## 🚀 Plug-and-play install
62
+
63
+ ```bash
64
+ pip install django-admin-rest-api
65
+ ```
66
+
67
+ Two changes to your project:
68
+
69
+ ```python
70
+ # settings.py
71
+ INSTALLED_APPS = [
72
+ # ... your existing apps ...
73
+ "django.contrib.admin",
74
+ "django_admin_rest_api", # ← add
75
+ ]
76
+ ```
77
+
78
+ ```python
79
+ # urls.py
80
+ from django.contrib import admin
81
+ from django.urls import include, path
82
+
83
+ urlpatterns = [
84
+ path("admin/", admin.site.urls),
85
+ path("admin-api/", include("django_admin_rest_api.urls")), # ← add
86
+ ]
87
+ ```
88
+
89
+ That's it. Your admin is now also a JSON API at `/admin-api/api/v1/...`.
90
+
91
+ ---
92
+
93
+ ## 📡 The endpoints
94
+
95
+ | Method | Path | What it returns |
96
+ | ------- | ---------------------------------------------- | -------------------------------------------------------- |
97
+ | `GET` | `/api/v1/registry/` | The same app/model tree Django renders in the admin index |
98
+ | `GET` | `/api/v1/schema/` | OpenAPI 3.1 schema of every endpoint below |
99
+ | `GET` | `/api/v1/<app>/<model>/` | List + pagination + filters + search |
100
+ | `POST` | `/api/v1/<app>/<model>/` | Create (runs the same `ModelForm`) |
101
+ | `GET` | `/api/v1/<app>/<model>/<pk>/` | Detail (read view as the HTML admin renders it) |
102
+ | `PATCH` | `/api/v1/<app>/<model>/<pk>/` | Update |
103
+ | `DELETE`| `/api/v1/<app>/<model>/<pk>/` | Destroy (with `LogEntry`) |
104
+ | `POST` | `/api/v1/<app>/<model>/bulk-update/` | Bulk patch |
105
+ | `POST` | `/api/v1/<app>/<model>/delete-preview/` | Cascade preview (like the HTML admin's confirm page) |
106
+ | `GET` | `/api/v1/<app>/<model>/autocomplete/?q=…` | `ModelAdmin.autocomplete_fields` source |
107
+ | `POST` | `/api/v1/<app>/<model>/actions/` | Run a `ModelAdmin` action on a selection |
108
+ | `GET` | `/api/v1/<app>/<model>/<pk>/history/` | The `LogEntry` history for one object |
109
+ | `GET` | `/api/v1/recent-actions/` | The dashboard's "Recent Actions" feed |
110
+ | `POST` | `/api/v1/login/` | Same `authenticate` + `login` as the HTML admin |
111
+ | `POST` | `/api/v1/logout/` | Same `logout` |
112
+ | `POST` | `/api/v1/<app>/<model>/<pk>/set-password/` | Auth-app `set_password` permission gate (User model) |
113
+
114
+ Every endpoint enforces the same permission gates as the HTML admin.
115
+
116
+ ---
117
+
118
+ ## 📸 Screenshots
119
+
120
+ The JSON `registry` endpoint — the source-of-truth for any consumer
121
+ frontend:
122
+
123
+ ![Registry endpoint JSON response](docs/screenshots/06-registry-api-json.png)
124
+
125
+ And here is the same admin rendered by
126
+ [`django-admin-react`](https://github.com/MartinCastroAlvarez/django-admin-react)
127
+ on top of this API, to give you an idea of what a consumer can build:
128
+
129
+ | | |
130
+ |:-:|:-:|
131
+ | ![SPA login](docs/screenshots/01-spa-login.png) | ![SPA registry](docs/screenshots/02-spa-registry.png) |
132
+ | ![SPA list](docs/screenshots/03-spa-list.png) | ![SPA detail](docs/screenshots/05-spa-detail.png) |
133
+
134
+ ---
135
+
136
+ ## ⚙️ Configuration
137
+
138
+ All settings live under a single optional dict — defaults are sane,
139
+ so most projects need no entry at all.
140
+
141
+ ```python
142
+ # settings.py (all keys optional)
143
+ DJANGO_ADMIN_REST_API = {
144
+ # Dotted path to the AdminSite whose ModelAdmin registry the API
145
+ # mirrors. Default exposes django.contrib.admin.site.
146
+ "ADMIN_SITE": "django.contrib.admin.site",
147
+
148
+ # Pagination. List endpoints use ModelAdmin.list_per_page as the
149
+ # source of truth; DEFAULT_PAGE_SIZE is the fallback. MAX_PAGE_SIZE
150
+ # caps ?page_size from the client (DoS guard).
151
+ "DEFAULT_PAGE_SIZE": 25,
152
+ "MAX_PAGE_SIZE": 200,
153
+
154
+ # When True, list responses include per-query timing in a debug
155
+ # block. Off by default — only enable in development.
156
+ "ENABLE_PROFILING": False,
157
+ }
158
+ ```
159
+
160
+ ---
161
+
162
+ ## 🔒 Security
163
+
164
+ - The API is **not** a parallel auth surface. It refuses any caller
165
+ the HTML admin would refuse, with the same gate
166
+ (`AdminSite.has_permission`, plus the per-model `ModelAdmin.has_*_permission`).
167
+ - Anonymous → `403` for every data endpoint.
168
+ - Authenticated but non-staff → `403`. Cookie present but resolved
169
+ user is anonymous → `403 not_authenticated`.
170
+ - Writes always go through `ModelForm.is_valid()` —
171
+ `unique_together`, `clean()`, field validators all run.
172
+ - Per-object guards run **before** the form does anything. The
173
+ `delete-preview` and `delete` endpoints both check `has_delete_permission(obj)`.
174
+ - CSRF is enforced everywhere. No view in this package is
175
+ `@csrf_exempt`. The login endpoint requires the CSRF cookie set
176
+ by the consumer's shell.
177
+
178
+ See the upstream
179
+ [`django-admin-react` SECURITY.md](https://github.com/MartinCastroAlvarez/django-admin-react/blob/main/SECURITY.md)
180
+ for the full threat model — the API surface is identical and the
181
+ guarantees transfer 1:1.
182
+
183
+ ---
184
+
185
+ ## 🧪 Local development
186
+
187
+ ```bash
188
+ git clone https://github.com/MartinCastroAlvarez/django-admin-api
189
+ cd django-admin-api
190
+ poetry install
191
+ poetry run pytest
192
+ poetry run ruff check .
193
+ poetry run black --check .
194
+ poetry run mypy django_admin_rest_api
195
+ poetry run bandit -c pyproject.toml -r django_admin_rest_api
196
+ ```
197
+
198
+ The test suite uses `pytest-django` + an in-memory SQLite database, so
199
+ no setup beyond `poetry install`.
200
+
201
+ ---
202
+
203
+ ## 🤝 Contributing
204
+
205
+ Issues, PRs, and Discussions are welcome on GitHub:
206
+ <https://github.com/MartinCastroAlvarez/django-admin-api>.
207
+
208
+ The lint + security gate is the same set the upstream
209
+ `django-admin-react` repo uses: **ruff, black, isort, flake8,
210
+ pylint, mypy, bandit, pip-audit, gitleaks.** Every change must pass
211
+ all of them before merge.
212
+
213
+ ---
214
+
215
+ ## 📜 License
216
+
217
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,18 @@
1
+ """django-admin-rest-api — a JSON REST API for the Django admin.
2
+
3
+ A frontend-agnostic Django app that exposes every ``ModelAdmin``
4
+ through JSON endpoints (list, detail, create, update, delete, actions,
5
+ history, autocomplete, …) using the **same permissions and the same
6
+ ModelAdmin source of truth** as the HTML admin.
7
+
8
+ This package adds no new features and no new permissions: it is the
9
+ wire surface that lets clients like
10
+ `django-admin-react <https://pypi.org/project/django-admin-react/>`_
11
+ and the forthcoming ``django-admin-mcp`` drive the Django admin over a
12
+ clean JSON contract.
13
+
14
+ See ``README.md`` for the install + URL wiring, and
15
+ ``docs/api-contract.md`` (in the repo) for the wire shape.
16
+ """
17
+
18
+ __version__ = "0.1.0a0"
@@ -0,0 +1,31 @@
1
+ # django_admin_rest_api/api/
2
+
3
+ JSON API package. See [`/docs/api-contract.md`](../../docs/api-contract.md)
4
+ for the wire format and [`/ARCHITECTURE.md`](../../ARCHITECTURE.md) §4 for
5
+ the design.
6
+
7
+ ## Rules
8
+
9
+ - Every view consults `ModelAdmin` for permissions, queryset, form,
10
+ serialization. No exceptions.
11
+ - No direct `Model.objects.all()` — start from
12
+ `ModelAdmin.get_queryset(request)`.
13
+ - Client-provided `app_label`/`model_name` are resolved through
14
+ `admin.site._registry` only.
15
+ - CSRF on unsafe methods. Never exempt yourself.
16
+ - Conservative serializer with `str()` fallback (see
17
+ `serializers.py`).
18
+ - A denylist of sensitive-shaped field names is applied on top of the
19
+ admin form's own exclusion (defense in depth).
20
+
21
+ ## Layout
22
+
23
+ | File | Purpose |
24
+ | ----------------- | ------------------------------------------------------------ |
25
+ | `urls.py` | URL patterns for all API endpoints. |
26
+ | `permissions.py` | Staff + AdminSite.has_permission gate; per-op delegation. |
27
+ | `registry.py` | AdminSite introspection helpers. |
28
+ | `serializers.py` | Conservative field serialization + denylist. |
29
+ | `views/` | One module per endpoint. |
30
+
31
+ Implementation status is tracked in `../README.md`.
@@ -0,0 +1,5 @@
1
+ """JSON API for django_admin_rest_api.
2
+
3
+ See `docs/api-contract.md` for the wire contract. All endpoints live
4
+ here; nothing in this subpackage may bypass ModelAdmin.
5
+ """