Skip to content

feat(api): surface ModelAdmin custom views to the SPA (#439)#539

Merged
MartinCastroAlvarez merged 1 commit into
mainfrom
feat/custom-views-439-135118
May 28, 2026
Merged

feat(api): surface ModelAdmin custom views to the SPA (#439)#539
MartinCastroAlvarez merged 1 commit into
mainfrom
feat/custom-views-439-135118

Conversation

@MartinCastroAlvarez
Copy link
Copy Markdown
Owner

Summary

Closes #439. Revives PR #465 (closed when the human-gate was in force; the user lifted that gate this session). Cherry-picked onto latest main, re-validated.

Custom admin views (ModelAdmin.get_urls / AdminSite.get_urls) were unreachable in the SPA. This surfaces a registered ModelAdmin's custom URLs on the detail response as a typed custom_views: [{name, label, url}] list and renders them as link-outs on the detail page — Option A (link-out), conservative and safe (no embedded rendering of an arbitrary admin view).

  • Backend: custom_views.py extracts the admin's custom URL specs (excluding the framework's standard change/history/delete views); detail view returns them; registry helper covers per-admin discovery.
  • Frontend: detail page renders the list; contract types updated.
  • Tests: test_custom_views.py covers discovery + the per-object link composition.

Per [[feedback-tier-5-6-override]], ship and you review post-merge.

Test plan

  • tests/test_custom_views.py + test_security.py + test_detail.py green (71).
  • Full vitest (142) + pnpm -r typecheck + eslint/dark-mode + ruff + mypy clean.

🤖 Generated with Claude Code

Many consumers add bespoke admin pages — reports, import/export,
per-object tools — via ModelAdmin.get_urls(). The SPA cannot reach
them. Introspect get_urls(), drop the five standard CRUD routes plus
the unnamed legacy catch-all, and surface every remaining named route
as {name, label, url, level}. Object-level routes (an object_id/pk
capture group) reverse with the object's pk; changelist-level routes
reverse with no args, all through the admin site's namespace.

Detail payload gains object- + changelist-level custom_views; the
registry model entry gains changelist-level ones only. The key is
omitted when empty. Everything is guarded — a misbehaving consumer
get_urls degrades to [] and never 500s. No new permission surface:
this rides the existing detail/registry staff + has_view_permission
gates.

Frontend: DetailPage renders an unobtrusive link-out (a single button,
or a "More" dropdown for several) of real <a target="_blank"> anchors
to the Django-rendered pages. Contract gains a CustomView type.

This is Option A (link-out), the design-safe foundation; the same
payload powers a future iframe/native approach. Reverse only resolves
while the legacy admin remains mounted.

Refs #439
@MartinCastroAlvarez MartinCastroAlvarez merged commit 308b9ee into main May 28, 2026
5 checks passed
@MartinCastroAlvarez MartinCastroAlvarez deleted the feat/custom-views-439-135118 branch May 28, 2026 13:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants