feat(spa): create — Add form completes CRUD (GET add-form endpoint + CreatePage)#199
Merged
Conversation
Owner
Author
🎯 PM / UX ✅ APPROVECompletes CRUD — the SPA can now create, not just read/edit/delete. Add button is permission-gated ( |
Owner
Author
🏛 Software Architect ✅ APPROVE
|
Owner
Author
🔒 Security & Compliance ✅ APPROVE
|
Owner
Author
🛒 Consumer / Customer ✅ APPROVEVerified live end-to-end: GET add-form 200 → POST 201 (new Group, redirect to detail) → DELETE 204 cleanup. The SPA is now full CRUD. This is the capability that lets a consumer add records, not just manage existing ones. Anonymisation: generic Django |
The SPA could read/edit/delete but not create. This adds the create path so a new object can be added entirely from /admin2/, completing core CRUD parity with the Django admin. Backend: - views/create_form.py — GET <app>/<model>/add/ returns the create-form schema (fieldsets + field descriptors) for a NEW object. Builds an unsaved instance + the ADD form (get_form(request, obj=None, change=False) — exactly how Django's add view constructs it) and reuses the detail view's descriptor builders so the field shape is byte-identical to edit (one FieldInput renders both). Gated on has_add_permission (create is gated on add, not view). Sensitive-name denylist applied. No pk/label/inlines (it's a new object). urls.py: `add/` route before the `<pk>` instance route. Frontend: - AddFormResponse contract type + ApiClient.addForm(). - CreatePage.tsx — fetches the add-form schema, renders the shared FieldInput form, POSTs via createObject, surfaces field-level validation errors, navigates to the new object's detail on success. - App.tsx — `:app/:model/add` route (literal `add` ranks above the `:pk` route in React Router, so it can't be mistaken for a detail). - ListPage — "+ Add <Model>" button in the header, shown only when permissions.add. Create goes through ModelAdmin.get_form() → is_valid() → save_model() on the backend (the existing POST path); the SPA only builds the payload + renders errors. Tests (tests/test_create_form.py): anon→redirect/403, non-staff→403, staff+add→200 with field descriptors, staff without add perm→403, unregistered→404, and the add form is built with change=False/obj=None (Django add-view contract). 22/22 create-form + detail tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
0cb85e0 to
2619839
Compare
MartinCastroAlvarez
pushed a commit
that referenced
this pull request
May 26, 2026
Ships the batch merged since 0.2.0a2: - SECURITY: SW message-handler origin check (#208, CodeQL js/missing-origin-check) — the artifact's service worker now rejects cross-origin cache-control messages. - PWA backend serving (#200), React login form end-to-end (#190), create/Add form completing CRUD (#199), autocomplete FK widget (#207), date_hierarchy drill-down (#205), action-label + list cosmetics fixes (#203/#204), lint cleanup (#202). 368 tests pass. Tier 6 — version bump; publish via the Security deploy-gate under the standing "deploy regularly if secure" directive. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MartinCastroAlvarez
added a commit
that referenced
this pull request
May 26, 2026
Ships the batch merged since 0.2.0a2: - SECURITY: SW message-handler origin check (#208, CodeQL js/missing-origin-check) — the artifact's service worker now rejects cross-origin cache-control messages. - PWA backend serving (#200), React login form end-to-end (#190), create/Add form completing CRUD (#199), autocomplete FK widget (#207), date_hierarchy drill-down (#205), action-label + list cosmetics fixes (#203/#204), lint cleanup (#202). 368 tests pass. Tier 6 — version bump; publish via the Security deploy-gate under the standing "deploy regularly if secure" directive. Co-authored-by: Martin Castro Laminrs <mcastro@laminr.ai> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Role: Author (Consumer / Customer agent). Author ≠ Reviewer ≠ Merger.
Completes core CRUD: the SPA could read/edit/delete (#192); this adds create.
Backend
views/create_form.py—GET <app>/<model>/add/returns the create-form schema (fieldsets + field descriptors) for a NEW object. Builds an unsaved instance + the ADD form (get_form(request, obj=None, change=False)— Django's add-view contract) and reuses the detail descriptor builders so the field shape is byte-identical to edit. Gated onhas_add_permission. Sensitive-name denylist applied. No pk/label/inlines.urls.py—add/route before the<pk>instance route.Frontend
AddFormResponsecontract type +ApiClient.addForm().CreatePage.tsx— fetches the schema, renders the sharedFieldInputform, POSTs viacreateObject, surfaces field errors, navigates to the new object on success.App.tsx—:app/:model/addroute (literaladdranks above:pk).permissions.add.Verified live (laminr pilot)
GET auth/group/add/+bank/bank/add/→ 200.POST auth/group/→ 201{pk, label, redirect};DELETEcleanup → 204.CRUD status
With this + #192 (edit/delete), the SPA now does create / read / update / delete end-to-end. Inline-write + file-upload + autocomplete widget remain (tracked in #160).
Roles
change=False/obj=None;add/route ordering.has_add_permission; create still flows throughModelAdmin.get_form()→is_valid()→save_model(); sensitive-name denylist applied; CSRF on the POST.Tier
Tier 4 — new read-only GET endpoint + SPA; no SECURITY.md/deps/workflow change.