| 12.2-admin-form-fields-date-file-upload-relation-editing-with-def |
03 |
admin |
| cabana |
| relation-manager |
| hasMany |
| belongsToMany |
| pivot |
| deferred-binding |
| fileupload |
| openapi |
|
| phase |
provides |
| 12.2-01 |
deferred_bindings store ops (DeferredBind/Unbind/Bindings/Forget/Slaves, DeferredEnvelope), MorphType, pact Relation{Before,After}{Create,Update,Delete} hooks, PurgeDeferred over Models() |
|
| phase |
provides |
| 12.2-02 |
X-Session-Key, fileScope and the seven file handlers, commitDeferred in CRUDService.save, fileupload/datepicker compile |
|
|
| RelationContract.Kind / ForeignKey with RelationHasMany and RelationBelongsToMany (empty Kind is belongsToMany) |
| manage.form / view.form / top-level form and pivot.form compiled against the related or pivot model; $/ paths inside the plugin |
| view toolbarButtons create|update|delete|link|unlink as route capabilities |
| child create/show/update/delete and pivot show/update routes, parent-scoped through loadChild (404 on a miss) |
| hasMany link/unlink, pivot values on link through the pivot form whitelist |
| record id 0 relation routes held against X-Session-Key and committed by the record's create save |
| child file routes keyed by X-Child-Session-Key (ChildSessionKeyHeader), committed by the child save |
| RelationSchema kind, deferrable, manageForm, viewForm, pivotForm; FormField.deferrable; 17 relation message keys |
| boot check that a deferrable relation with create has its related model in some plugin's Models() |
|
| 12.2-04 admin SPA (child |
| pivot and create-screen UI on these routes) |
| 12.2-05 unit and security tests |
|
| tokens |
tasks |
commits |
| 82200 |
3 |
3 |
|
0b4ef9c311 |
fe9e8baaf1 |
| added |
patterns |
|
|
| A relation form is compiled as a CompiledController of its own (relationModelController around the related or pivot model), so Fill, Validate, file and date pipelines are reused unchanged |
| Every child, pivot and child-file query carries the parent predicate (FK, pivot EXISTS, or the session's bound slaves); a miss is recordNotFound |
| File handlers run on a fileRoute (fields, key header, scope), shared by record and relation child file routes |
| Link and unlink go through linkRelated/unlinkRelated, used by the routes and by the deferred commit |
|
|
| created |
modified |
| modules/cabana/relation_form.go |
| modules/cabana/relation_child.go |
| modules/cabana/relation_child_smoke_test.go |
| modules/cabana/example_relation_test.go |
|
| modules/cabana/relation.go |
| modules/cabana/deferred.go |
| modules/cabana/field_file.go |
| modules/cabana/http.go |
| modules/cabana/registry.go |
| modules/cabana/schema.go |
| modules/cabana/form_schema.go |
| modules/cabana/schema_types.go |
| modules/cabana/messages.go |
| modules/cabana/admin_openapi.go |
| modules/cabana/security_coverage_test.go |
| modules/cabana/openapi_conformance_test.go |
| modules/cabana/phase10_csrf_test.go |
| modules/cabana/phase10_coverage_test.go |
| modules/cabana/datepicker_smoke_test.go |
| modules/phrasebook/backend/lang/en/lang.yaml |
| modules/phrasebook/backend/lang/pl/lang.yaml |
| admin/openapi/admin.json |
| admin/src/api/schema.d.ts |
| admin/tests/fixtures/widgets.relation-schema.json |
| modules/cabana/README.md |
| docs/backend/relation-manager.md |
|
|
| 12.2-03: a relation form is compiled into its own CompiledController (relationModelController), so the child save reuses projectOperation, fillAllowed, mergedRules, dateBoundDetails and commitDeferred |
| 12.2-03: a hasMany child's ForeignKey is set from the scoped parent before Fill and Validate, so a model rule on the key passes; a body naming the key is dropped |
| 12.2-03: pivot values are filled through the pivot form's writable fields only (the pivot model needs no Fillable), and only the rules of those fields are checked |
| 12.2-03: unknown pivot keys are a 422 on PUT .../pivot/{child} as on link, not silently dropped |
| 12.2-03: at commit a bind of a child the session created attaches directly (hasMany: key set if still NULL; belongsToMany: pivot row with RelationBeforeLink); a bind of an existing record re-runs the full link eligibility |
| 12.2-03: unlink on an unsaved parent only cancels ids bound in the session; no stray unbind rows are written |
| 12.2-03: child file routes on a saved child answer 403 without update (writes) or without update or a view form (reads); child 0 without create is 404, as a record's id 0 |
| 12.2-03: the Models() boot check matches the related model by Go type, or by morph type when a database is published |
| 12.2-03: the pending-created exclusion matches pivot_data LIKE '{"created":true%', the envelope's encoded prefix, so a malformed pivot_data row cannot break candidate queries |
|
| relationParent: a saved record (id) or the unsaved one (DeferredKey + related morph); relationQuery and loadChild branch on it |
| Route capability: relationAllowed(cr predicate) writes 404 for an unknown relation and 403 for an undeclared capability before any SQL |
|
|
| id |
description |
requirement |
verification |
human_judgment |
| D1 |
A hasMany child created under gadget A carries A's id, lists under A only, and a body naming gadget_id cannot move it |
SC-3 |
| kind |
ref |
status |
| integration |
modules/cabana/relation_child_smoke_test.go#TestRelationChildSmokeCreate |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D2 |
Show, update, delete and pivot routes answer 404 for a child of another parent and for a FormExtendQuery-hidden parent; a delete naming a foreign child deletes nothing; hasMany link/unlink/delete |
SC-3 |
| kind |
ref |
status |
| integration |
modules/cabana/relation_child_smoke_test.go#TestRelationChildSmokeScope |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D3 |
Link with a pivot note stores it, RelationBeforeLink still stamps its hook column, pivot keys outside the form and multi-id pivot links are 422 |
SC-3 |
| kind |
ref |
status |
| integration |
modules/cabana/relation_child_smoke_test.go#TestRelationChildSmokePivot |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D4 |
On id 0 a created part and a member linked with a pivot note are attached by the gadget's create save; an unlinked pending part is deleted; no binding is left |
SC-4 |
| kind |
ref |
status |
| integration |
modules/cabana/relation_child_smoke_test.go#TestRelationChildSmokeDeferredCreate |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D5 |
A deferred link the saved gadget excludes answers 422 on the relation-manager field, nothing is created, the binding stays |
SC-4 |
| kind |
ref |
status |
| integration |
modules/cabana/relation_child_smoke_test.go#TestRelationChildSmokeDeferredRollback |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D6 |
An image uploaded to child 0 with X-Child-Session-Key is attached to the part its create makes |
SC-4 |
| kind |
ref |
status |
| integration |
modules/cabana/relation_child_smoke_test.go#TestRelationChildSmokeChildFile |
pass |
|
|
false |
|
| id |
description |
requirement |
verification |
human_judgment |
| D7 |
Boot stops when a deferrable relation with create has no Models() entry for its related model |
SC-4 |
| kind |
ref |
status |
| unit |
modules/cabana/relation_child_smoke_test.go#TestRelationChildSmokePurgeModels |
pass |
|
|
false |
|
| id |
description |
verification |
human_judgment |
| D8 |
All 13 new routes inventoried, guarded, CSRF-walked, in admin.json and called once by the conformance suite; SPA types, fixture and dist in sync |
| kind |
ref |
status |
| integration |
modules/cabana#TestPhase09PermissionMatrix|TestPhase09ContractInventory|TestPhase10CSRF|TestPhase10Coverage|TestPhase10OpenAPIConformance |
pass |
|
| kind |
ref |
status |
| other |
scripts/check-admin-openapi.sh --check && scripts/check-admin-dist.sh && npm --prefix admin test |
pass |
|
|
false |
|
| id |
description |
verification |
human_judgment |
| D9 |
Existing belongsToMany contracts unchanged: fonoteka.go builds, vets and passes its admin tests with no change |
| kind |
ref |
status |
| integration |
go -C ../fonoteka.go build ./... && go -C ../fonoteka.go vet ./... && go -C ../fonoteka.go test ./plugins/golem15/fonoteka -run Admin |
pass |
|
|
false |
|
| id |
description |
verification |
human_judgment |
| D10 |
Docs and README name only existing identifiers and commands |
| kind |
ref |
status |
| other |
go test ./cmd/summer -run TestDocsTree && go run ./cmd/summer docs:build --check |
pass |
|
|
false |
|
|
45min |
2026-10-02 |
complete |