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 ObservationStatus — final, 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 SubjectType — patient, 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.