Clinical care
Meta Artifact
A MetaArtifact attaches a freeform metadata payload to any MANSOQ object without a dedicated foreign key. You reach for it when a feature needs to hang structured data — a drawing on a patient, say — off an existing record without adding a…
3.1 · versioned documentation · 5 min read
A MetaArtifact attaches a freeform metadata payload to any MANSOQ object without a dedicated foreign key. You reach for it when a feature needs to hang structured data — a drawing on a patient, say — off an existing record without adding a column or a join table.
Storage and schema split across two layers. The Django model stays loose: object_value is an opaque JSONField, and associating_type / object_type are bare CharFields. The Pydantic resource specs constrain those values to known enums and define the request/response shapes the API accepts.
Source:
- Model:
care/emr/models/meta_artifact.py - Resource specs:
care/emr/resources/meta_artifact/spec.py
Models
| Model | Purpose |
|---|---|
MetaArtifact |
Generic, polymorphic metadata record attached to any MANSOQ object by type + external ID |
MetaArtifact extends EMRBaseModel, the shared MANSOQ EMR base that provides external_id, the audit fields created_by / updated_by and created_date / modified_date, and the soft-delete flag deleted.
MetaArtifact fields
Association
There is no Django ForeignKey to a parent. A MetaArtifact points at its target by storing the target's type and external_id instead.
| Field | Type | Notes |
|---|---|---|
associating_type |
CharField(255) |
Non-null. The kind of object this artifact hangs off. API-constrained to MetaArtifactAssociatingTypeChoices |
associating_external_id |
UUIDField |
Non-null. The parent's external_id. Set server-side from the spec's associating_id (see Resource specs) |
A composite database index covers these two fields (see Methods & save behaviour), so looking up every artifact for a given parent stays cheap.
Content
| Field | Type | Notes |
|---|---|---|
name |
CharField(255) |
Spec rejects blank/whitespace-only on create |
object_type |
CharField(255) |
Names the shape held in object_value. API-constrained to MetaArtifactObjectTypeChoices |
object_value |
JSONField |
The payload. Spec types it as dict | list, required with no default |
note |
TextField |
Nullable. Spec: str | None, default None |
Enum values
Two str enums constrain associating_type and object_type at the API layer. Both live in spec.py — this resource has no separate constants.py or valueset.py.
MetaArtifactAssociatingTypeChoices values
Binds associating_type.
| Value |
|---|
patient |
encounter |
MetaArtifactObjectTypeChoices values
Binds object_type.
| Value |
|---|
drawing |
Resource specs (API schema)
All specs extend EMRResource (care/emr/resources/base.py), which provides serialize (model → pydantic) and de_serialize (pydantic → model) along with the perform_extra_serialization / perform_extra_deserialization hooks. __model__ = MetaArtifact.
| Spec | Role | Fields exposed |
|---|---|---|
MetaArtifactBaseSpec |
shared | id (UUID4 | None), object_value (dict | list), note (str | None) |
MetaArtifactCreateSpec |
write · create | base + associating_type, associating_id (UUID4), object_type, name |
MetaArtifactUpdateSpec |
write · update | identical to MetaArtifactBaseSpec (only object_value / note are mutable; association and type are fixed after create) |
MetaArtifactReadSpec |
read · detail/list | base + associating_type, associating_id, object_type, name, created_date, modified_date, created_by (dict | None), updated_by (dict | None) |
Field-level details and validation:
| Field | Spec type | Required | Default | Validation / behaviour |
|---|---|---|---|---|
id |
UUID4 | None |
optional | None |
Read-only; populated from obj.external_id in perform_extra_serialization |
object_value |
dict | list |
yes | — | Arbitrary JSON object or array |
note |
str | None |
no | None |
Free text |
associating_type |
MetaArtifactAssociatingTypeChoices |
yes (create) | — | patient or encounter |
associating_id |
UUID4 |
yes (create) | — | Parent's external_id; copied to model field associating_external_id in perform_extra_deserialization |
object_type |
MetaArtifactObjectTypeChoices |
yes (create) | — | drawing |
name |
str |
yes (create) | — | validate_name rejects blank/whitespace-only with "Name cannot be empty" |
created_date |
datetime |
read | — | From EMRBaseModel |
modified_date |
datetime |
read | — | From EMRBaseModel |
created_by / updated_by |
dict | None |
read | None |
Serialized via serialize_audit_users (cached UserSpec) |
Server-maintained behaviour:
- Create (
MetaArtifactCreateSpec.perform_extra_deserialization): setsobj.associating_external_id = self.associating_id. Clients send the parent'sexternal_idasassociating_id; the model stores it underassociating_external_id. - Read (
MetaArtifactReadSpec.perform_extra_serialization): setsmapping["id"] = obj.external_idand callsserialize_audit_usersto attachcreated_by/updated_by. - Update:
MetaArtifactUpdateSpecexposes onlyobject_valueandnote(plus the inherited optionalid). It never re-acceptsassociating_type,associating_id,object_type, orname, so association and classification are immutable once the record exists.
No field on this resource binds to a value set (there is no valueset.py); the only constrained vocabularies are the two str enums above.
Methods & save behaviour
MetaArtifact adds no custom methods and overrides neither save() nor delete(). Audit and soft-delete behaviour come entirely from EMRBaseModel.
Its Meta declares one composite index to back parent-object lookups:
Index(fields=["associating_type", "associating_external_id"])
API integration notes
- Reference the target by its
external_id(UUID), never its internal primary key, and pass it asassociating_idon create. Theassociating_type+associating_external_idpair lets one table hold metadata for many MANSOQ object types without per-type foreign keys. - Add a new association or artifact kind by extending the
strenums inspec.py— no schema migration, since the underlyingCharFields are already open. object_valueaccepts anydict | list, so payloads carry feature-specific structure without migrations;object_typerecords which shape to expect (e.g.drawing).- After creation,
associating_type,associating_id,object_type, andnameare fixed. Onlyobject_valueandnotecan change, viaMetaArtifactUpdateSpec. EMRBaseModelmaintains the audit fields (created_by,updated_by,created_date,modified_date), the publicexternal_id(exposed asid), and soft-delete (deleted). Don't set them from clients.
Related
- Base model: EMRBaseModel
- Common patient associations: Patient, Encounter
- Model source: meta_artifact.py on GitHub
- Spec source: meta_artifact/spec.py on GitHub
Source evidence
References are pinned to the private MANSOQ source repository. Repository access is required to open these links.