Skip to content

Comments and logged time

Two needs come up on almost every model: discussing a record, and noting the time spent on it. Both exist, both are generic, and both switch on with a word.

registry.register_model(
"library_title",
module="library",
taggable=True,
commentable=True, # a comment thread
worklog=True, # a time log
fields=[...],
)

No table to create, no column added. Both stores are polymorphic: a single kws_thread_message table and a single kws_worklog table, keyed by (model, id). The flag does one thing: declare that your model is a legitimate subject.

The comment thread

POST /api/v1/threads/records/library_title/1
{"body": "Damaged copy, needs re-gluing."}
-> {"id": 1}
GET /api/v1/threads/records/library_title/1
-> {"messages":[{"id":1,"author_id":1,"body":"Damaged copy, needs re-gluing.",
"created_at":"2026-09-06T19:32:02+00:00","edited_at":null}],
"count":1}

One endpoint, for any commentable model. edited_at tells a corrected message from an original one, and only its author may change it: the server answers 403 to anyone else, administrators included.

The time log

POST /api/v1/worklog/records/library_title/1
{"work_date": "2026-09-06", "duration_minutes": 45,
"description": "Re-gluing the binding"}
-> {"id": 1}
GET /api/v1/worklog/records/library_title/1
-> {"entries":[{"id":1,"user_id":1,"work_date":"2026-09-06","duration_minutes":45,
"description":"Re-gluing the binding","billable":false}],
"total_minutes":45}

The duration is in whole minutes, never decimal hours: 1.5 hours has exactly one spelling in minutes, and not three possible roundings. The total is computed by the server, not by the screen that shows it.

The flag is a refusal, not a decoration

A model without the flag is not “without comments”: it is refused.

GET /api/v1/worklog/records/library_copy/1 -> 404

library_copy exists, the record exists, and the server still answers 404. That is deliberate: the generic surface must not become a free store where any client hangs data off any table. You decide, model by model, what may carry a discussion or time.