You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Five audit findings, all README-only — no code change:
- **#623 / #624 / #625 / #626 / #627 / #628 / #630 / #622:** Add a
"Stock-Django ModelAdmin hooks that do NOT carry through to the
SPA" table. Documents the silent-noop hooks (template overrides,
formfield_overrides, filter_horizontal, GFK, i18n) and the
partially-honoured hints (raw_id_fields + radio_fields — the API
emits the hint, the SPA still renders autocomplete / dropdown).
Corrects the existing feature-status table where `raw_id_fields`
was incorrectly marked ✅.
- **#633:** "Writing safe `list_display` callables" — bans the
`mark_safe(f"...")` foot-gun + shows the `format_html`
auto-escape equivalent. Same advice applies on legacy `/admin/`
too, but the SPA's `dangerouslySetInnerHTML` render path makes the
consequence visible end-to-end.
- **#634:** "Hardening" section with a worked `django-axes`
integration pointed at the SPA's JSON login endpoint.
- **#635:** "Mounting the API on a different origin (CORS +
cookies)" — the three-setting matrix consumers have to configure
together (`SESSION_COOKIE_SAMESITE = None` + secure, CORS,
CSRF_TRUSTED_ORIGINS).
Tracking issues stay open until each underlying gap closes; the
README now sets honest expectations so consumers don't ship a
release thinking these all work invisibly.
Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|`raw_id_fields` (pk text input + lookup popup) | 🟡 [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) (API emits the hint; SPA still renders autocomplete) |
590
+
|`radio_fields` (inline radio buttons vs `<select>`) | 🟡 [#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626) (API emits the hint; SPA still renders dropdown) |
@@ -601,6 +603,121 @@ all share the v1 wire contract. Per-feature live status below.
601
603
602
604
✅ = shipped. 🟡 = not yet built (tracked).
603
605
606
+
### Stock-Django `ModelAdmin` hooks that do NOT carry through to the SPA
607
+
608
+
The SPA renders from the JSON wire — it never sees the consumer's
609
+
Django HTML templates, custom widgets, or `get_urls()` views. The
610
+
hooks below are stock-Django extension points the SPA cannot honour
611
+
today; if your admin uses any of them, the surface behaves
612
+
differently on the SPA than on the legacy `/admin/`. Tracking
613
+
issues link the work to close each gap.
614
+
615
+
| Stock-Django hook | SPA behaviour | Tracked |
616
+
|---|---|---|
617
+
|`change_form_template` / `change_list_template` / `add_form_template` / `change_password_template` / `object_history_template` overrides | Silently ignored — the SPA renders entirely from the JSON wire. |[#624](https://github.com/MartinCastroAlvarez/django-admin-react/issues/624)|
618
+
|`formfield_overrides = {Field: {"widget": CustomWidget}}`| Custom widget invisible — the SPA picks its own control from the field's `type`. No React-side widget-registration API yet. |[#625](https://github.com/MartinCastroAlvarez/django-admin-react/issues/625)|
619
+
|`raw_id_fields`| Falls back to the autocomplete picker (same as `autocomplete_fields`). Defeats the purpose for FKs with 10M+ rows where autocomplete `get_search_results` is too expensive. |[#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626)|
620
+
|`radio_fields = {"status": admin.HORIZONTAL}`| Renders a `<select>` (default choice control) instead of inline radio buttons. |[#626](https://github.com/MartinCastroAlvarez/django-admin-react/issues/626)|
621
+
|`filter_horizontal` / `filter_vertical` (M2M shuttle widget) | Renders the generic multi-select checkbox list, not Django's two-pane shuttle. Switch the field to `autocomplete_fields` for a workable SPA UX. |[#627](https://github.com/MartinCastroAlvarez/django-admin-react/issues/627)|
622
+
|`GenericForeignKey` / `GenericInlineModelAdmin`| Support gap — verify per-model before relying on the SPA. |[#628](https://github.com/MartinCastroAlvarez/django-admin-react/issues/628)|
623
+
|`LANGUAGE_CODE` / `gettext` / `Accept-Language`| The SPA chrome stays English; translated `verbose_name` / `help_text` / `@admin.action(description=_("..."))` are not surfaced per-request. |[#630](https://github.com/MartinCastroAlvarez/django-admin-react/issues/630)|
624
+
|`ModelAdmin.get_urls()` custom views | Opens as a popout (`<a target="_blank">`) into the Django-rendered HTML page — no SPA chrome, no breadcrumb. The link IS surfaced; the UX is just outside the SPA. |[#623](https://github.com/MartinCastroAlvarez/django-admin-react/issues/623)|
625
+
| Django 4.2 LTS support | Not yet — the package pins `django >= 5.0,<7.0`. |[#622](https://github.com/MartinCastroAlvarez/django-admin-react/issues/622)|
626
+
627
+
If your admin relies on any "silently ignored" hook above, the
628
+
typical workaround is to keep that model on the legacy
629
+
`/admin/` surface via the
630
+
[experience-toggle strip](#experience-toggle-strip-optional) — the
631
+
SPA + legacy admin happily coexist.
632
+
633
+
---
634
+
635
+
## Writing safe `list_display` callables
636
+
637
+
This applies on **both** the legacy `/admin/` and the SPA — but the
638
+
SPA renders any `format_html` / `mark_safe` value via React's
639
+
`dangerouslySetInnerHTML`, so misuse is reflected XSS the same way
640
+
the legacy admin would be.
641
+
642
+
**Do not** interpolate user-controlled data into a `mark_safe(...)`
643
+
string. The whole point of `mark_safe` is "I have already escaped
644
+
this," and `f"<span>{obj.user_input}</span>"` has not — so a
0 commit comments