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 -> 404library_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.