Skip to main content
Version: 3.0

Observation

An Observation is one named, coded data point about a subject — almost always a patient: a blood pressure reading, a temperature, a coded answer captured through a questionnaire. main_code records what was observed (usually bound to LOINC) and value holds the result. You rarely create one by hand; most observations are materialized when a questionnaire is submitted.

Source:

The model is just storage; several fields are opaque JSONFields. Their real structure lives in the Pydantic resource specs under care/emr/resources/observation/, which own the enums, nested JSON shapes, validation, and the read/write schema split. Each field row below gives the storage column first, then the shape the spec enforces on top of it.

Models

ModelPurpose
ObservationA single coded measurement or finding about a patient, captured within an encounter

Observation extends EMRBaseModel, the shared Care EMR base that supplies external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON.

Observation fields

Classification & coding

FieldStorage typeSpec shape / notes
statusCharField(255)Enum ObservationStatusfinal, amended, entered_in_error. Required in specs. Questionnaire-sourced observations land as final
is_groupBooleanFieldDefault False. True marks a grouping/panel observation that holds component/child observations instead of a single value. Not exposed in the resource specs
categoryJSONField (default {})Spec: optional Coding { system?, version?, code, display? }. FHIR observation category, derived from the questionnaire
main_codeJSONField (default {})Spec: optional Coding. What is being observed, typically LOINC (CARE_OBSERVATION_VALUSET, system http://loinc.org)
alternate_codingJSONField (default [])Spec: optional CodeableConcept { id?, coding?: Coding[], text }. Same concept expressed in other code systems

Subject & context

FieldStorage typeSpec shape / notes
subject_typeCharField(255)Enum SubjectTypepatient, encounter. Spec defaults to encounter
subject_idUUIDFieldThe subject entity's id. Set server-side; not in the spec body
patientFK → PatientCASCADE. Set server-side, not in the spec body
encounterFK → EncounterCASCADE. Spec field is UUID4 | None. Where the observation was recorded
effective_datetimeDateTimeFieldWhen the observation was effective. Nullable in storage, but the spec requires a tz-aware datetime on create; optional on update

Author & performer

FieldStorage typeSpec shape / notes
data_entered_byFK → users.UserCASCADE, nullable. related_name="observations_entered". Write specs accept data_entered_by_id: int; read specs serialize a nested UserSpec
performerJSONField (default {})Spec: optional Performer { type: PerformerType (related_person|user), id: str }. Use it when the performer differs from the data-entry user

Value

FieldStorage typeSpec shape / notes
value_typeCharField(255)Enum QuestionType (from the questionnaire module). Tells the client how to read value — see the table below. Required in specs
valueJSONFieldSpec: QuestionnaireSubmitResultValue { value?: str, unit?: Coding, coding?: Coding }. Required on create, optional on update. value holds the raw/string result, unit the quantity unit, coding the coded answer
noteTextFieldSpec: str | None. Free-text note

Clinical qualifiers

FieldStorage typeSpec shape / notes
body_siteJSONField (default {})Spec: optional Coding bound to CARE_BODY_SITE_VALUESET (slug system-body-site-observation, SNOMED CT). FHIR Observation.bodySite
methodJSONField (default {})Spec: optional Coding bound to CARE_OBSERVATION_COLLECTION_METHOD (slug system-collection-method, SNOMED CT). FHIR Observation.method
reference_rangeJSONField (default [])Spec: ReferenceRange[] (shape below), default []. Reference ranges for the value, usually carried over from the observation definition
interpretationJSONField (default {})Spec: free dict, default {}. Coded interpretation against the reference range (high/low/normal). Normal/abnormal/critical value sets exist (below) but are not enforced on this dict
interpretation_oldCharField(255)Nullable. Legacy free-text interpretation, kept for backward compatibility. Not in the specs

Structure & relationships

FieldStorage typeSpec shape / notes
parentUUIDFieldNullable. Spec: UUID4 | None — the external_id of a parent observation, for nesting related observations
componentJSONField (default [])Spec: Component[] (shape below), default []. Sub-observations sharing this context but carrying their own codes and values (FHIR Observation.component)
questionnaire_responseFK → QuestionnaireResponseCASCADE, nullable. Spec field is UUID4 | None. The submission that produced this observation
diagnostic_reportFK → DiagnosticReportCASCADE, nullable. The report this observation belongs to, if any. Not in the write specs
observation_definitionFK → ObservationDefinitionCASCADE, nullable. The definition this observation templates from. Serialized as BaseObservationDefinitionSpec only in ObservationRetrieveSpec

Enum & value-set reference

ObservationStatus values

ValueMeaning
finalRecorded, complete and verified. The default for questionnaire-sourced observations
amendedWas final, then corrected
entered_in_errorRecorded in error; disregard it

PerformerType values (performer.type)

Value
related_person
user

SubjectType values (subject_type)

Value
patient
encounter (spec default)

QuestionType values (value_type)

From the questionnaire module. The value tells the client which key of value to read.

ValueValue sourced from QuestionnaireSubmitResultValue
groupgrouping node (no direct value)
booleanvalue ("true"/"false")
decimalvalue
integervalue
stringvalue
textvalue
displaydisplay-only
datevalue
dateTimevalue
timevalue
choicecoding
urlvalue
quantityvalue + unit
structuredstructured payload

Bound value sets

Coded fields validate against registered Care value sets. A body_site or method coding outside its value set is rejected during deserialization.

FieldValue setSlugSystem(s)
main_codeCARE_OBSERVATION_VALUSETsystem-observationhttp://loinc.org
body_siteCARE_BODY_SITE_VALUESETsystem-body-site-observationhttp://snomed.info/sct (curated concept list + descendants of 442083009)
methodCARE_OBSERVATION_COLLECTION_METHODsystem-collection-methodhttp://snomed.info/sct (descendants of 272394005, 129264002, 386053000)

These interpretation value sets are registered for the frontend and observation definitions, but are not enforced on the interpretation dict:

Value setSlugConcepts (system http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation)
Normalsystem-normal-coded-valuesetN Normal, ND Not detected, NEG Negative
Abnormalsystem-abnormal-coded-valuesetA Abnormal, AA Critically abnormal, H High, HH Critically high, L Low, LL Critically low, POS Positive, DET Detected
Criticalsystem-critical-coded-valuesetC Critical, CC Critical high, CL Critical low, CV Critical value
UCUM unitssystem-ucum-unitshttp://unitsofmeasure.org (all UCUM units)

Nested JSON shapes (from the spec)

The structures behind the model's JSONFields.

Coding

Coding {
system: str | null # e.g. http://loinc.org
version: str | null
code: str # required
display: str | null
} # extra keys forbidden

CodeableConcept (alternate_coding)

CodeableConcept {
id: str | null
coding: Coding[] | null
text: str | null # field present, required key
} # extra keys forbidden

QuestionnaireSubmitResultValue (value)

QuestionnaireSubmitResultValue {
value: str | null # raw/string value
unit: Coding | null # for quantity values
coding: Coding | null # for coded values
}

Performer (performer)

Performer {
type: PerformerType # related_person | user
id: str
}

ReferenceRange (reference_range[])

ReferenceRange {
min: float | null
max: float | null
unit: str | null
interpretation: str # required
value: str | null
}

Component (component[])

Component {
value: QuestionnaireSubmitResultValue # required
interpretation: str | dict = {}
reference_range: ReferenceRange[] = []
code: Coding | null = null
note: str = ""
}

Resource specs (API schema)

The Pydantic specs build on EMRResource, which provides serialize / de_serialize and the perform_extra_serialization / perform_extra_deserialization hooks. Every concrete spec inherits from BaseObservationSpec.

SpecRoleNotes
BaseObservationSpecshared baseDefines the common fields: id, status, category, main_code, alternate_coding, subject_type, encounter, effective_datetime, performer, value_type, value, note, body_site (bound), method (bound), reference_range, interpretation, parent, questionnaire_response, component
ObservationSpecwrite · createAdds data_entered_by_id, created_by_id, updated_by_id (all int). Used internally when materializing observations from a questionnaire submission
ObservationUpdateSpecwrite · updateThe base, but effective_datetime and value relax to optional (None) for partial updates
ObservationReadSpecread · listSwaps the user IDs for nested created_by / updated_by / data_entered_by UserSpec objects
ObservationRetrieveSpecread · detailExtends ObservationReadSpec and additionally serializes observation_definition via BaseObservationDefinitionSpec when it is set

Server-side behaviour

  • ObservationSpec.perform_extra_deserialization (create/update): sets obj.external_id = self.id, copies data_entered_by_id, created_by_id, updated_by_id onto the model, drops data_entered_by_id from meta, and on create (is_update is false) clears obj.id so a new row is inserted.
  • ObservationReadSpec.perform_extra_serialization: maps id from external_id; deliberately blanks encounter, patient, and questionnaire_response to None in the output to avoid extra DB queries; resolves the audit users (created_by, updated_by) and data_entered_by from cache (model_from_cache).
  • ObservationRetrieveSpec.perform_extra_serialization: calls the parent, then inlines the full observation_definition when the FK is set.
  • body_site / method are ValueSetBoundCoding[...]: the supplied Coding is validated against its bound value set during deserialization and rejected if the code is not a member.
  • de_serialize only writes fields present in the model's column mapping. Non-default values are persisted (model_dump(exclude_defaults=True)), and unknown spec-only fields would land in meta when __store_metadata__ is set — which, by default, it is not for observations.

Observation is self-contained; it expresses relationships through foreign keys rather than secondary tables.

patient → FK Patient (CASCADE)
encounter → FK Encounter (CASCADE)
data_entered_by → FK users.User (CASCADE, nullable)
questionnaire_response → FK QuestionnaireResponse (CASCADE, nullable)
diagnostic_report → FK DiagnosticReport (CASCADE, nullable)
observation_definition → FK ObservationDefinition (CASCADE, nullable)

parent is the exception: a soft, UUID-based self-reference to another observation's external_id, not a Django FK. It lets observations nest without a database constraint.

API integration notes

  • Observations are exposed through Care's REST API and align with the FHIR Observation resource. FHIR field names (effectiveDateTime, bodySite, and so on) differ from the Django model names; the resource specs above are the authoritative request/response schemas.
  • Most observations are created as a side effect of submitting a questionnaire. value and value_type mirror the submission types (QuestionnaireSubmitResultValue / QuestionType), and questionnaire_response records provenance.
  • effective_datetime must be timezone-aware. Required on create, optional on update.
  • status is one of final / amended / entered_in_error; questionnaire-sourced observations are final.
  • body_site and method are validated against their bound value sets, and main_code is expected to bind to LOINC via CARE_OBSERVATION_VALUSET.
  • On read, the list and detail specs blank out encounter, patient, and questionnaire_response, so don't rely on those being populated in observation payloads.
  • Prefer structured observations. Allergy and medication checks read coded values, not free text, so unstructured data leaves them blind.