Skip to main content
Version: 3.0

Medication Statement

A MedicationStatement records a medication the patient is taking, has taken, or intends to take, as reported by the patient, a relative, or a clinician rather than actively prescribed or dispensed. Reach for it when you're capturing a medication history rather than driving an order. It aligns with the FHIR MedicationStatement resource.

The Django model stores medication and effective_period as opaque JSONFields. Their real structure comes from the Pydantic resource specs in care/emr/resources/medication/statement/, which define the enums, nested JSON shapes, validation, the value-set binding on medication, and the read/write API schemas. This page covers both layers.

Source:

Models​

ModelPurpose
MedicationStatementA record of a medication the patient is taking, has taken, or intends to take — as reported rather than actively prescribed

MedicationStatement extends EMRBaseModel, the shared Care EMR base that supplies external_id, audit fields created_by/updated_by, created_date/modified_date, history/meta JSON, and soft-delete semantics.

MedicationStatement fields​

Type is the Django storage type. Spec shape is the structured shape the Pydantic resource spec enforces when reading and writing over the API.

Statement details​

FieldTypeSpec shapeNotes
statusCharField(100)MedicationStatementStatus enumRequired. See Status values.
reasonCharField(100)str | NoneOptional. Why the medication is being taken.
medicationJSONField (default=dict)Coding bound to the system-medication value setRequired on create. A single SNOMED CT Coding, not a full CodeableConcept. See Coding shape and Bound value set.
information_sourceCharField(100)MedicationStatementInformationSourceType enum | NoneOptional. Who supplied the statement. See Information-source values.
effective_periodJSONField (default=dict)PeriodSpec | NoneOptional. The period the medication is or was taken. See PeriodSpec shape.
dosage_textTextFieldstr | NoneOptional, free-text dosage instructions. The spec flags that a structured Dosage may replace this later.
noteTextFieldstr | NoneOptional, free-text annotation.

Relationships​

FieldTypeNotes
patientFK → Patienton_delete=CASCADE. The patient the statement is about. Derived server-side from the encounter — never accepted from clients.
encounterFK → Encounteron_delete=CASCADE. The encounter the statement was recorded in. Supplied as a UUID on create.
patient → FK Patient (CASCADE)
encounter → FK Encounter (CASCADE)

Deleting a patient or encounter deletes its medication statements.

Enum values​

MedicationStatementStatus values​

MedicationStatementStatus(str, Enum) in spec.py, bound to status.

ValueMeaning
activeThe medication is being taken
on_holdTaking has been paused
completedThe course has finished
stoppedThe medication was stopped
unknownStatus is not known
entered_in_errorThe record was entered in error
not_takenThe patient is not taking the medication
intendedThe patient intends to take the medication

MedicationStatementInformationSourceType values​

MedicationStatementInformationSourceType(str, Enum) in spec.py, bound to information_source.

ValueMeaning
related_personA relative or related person reported it
practitionerA clinician reported it
patientThe patient self-reported it

Nested JSON shapes​

Coding shape​

medication is stored as JSON but typed as a single Coding (from care/emr/resources/common/coding.py), not a CodeableConcept. The spec sets extra="forbid", so unrecognized keys are rejected.

FieldTypeNotes
systemstr | NoneCode system URI (e.g. http://snomed.info/sct)
versionstr | NoneCode-system version
codestrRequired. Validated against the bound value set
displaystr | NoneHuman-readable label

Bound value set​

medication is declared as ValueSetBoundCoding[CARE_MEDICATION_VALUESET.slug]. On write, code is validated against the system-medication value set (medication.py), which includes SNOMED CT concepts under the constraint << 763158003 |Medicinal product|. Any code outside the set is rejected.

The care/emr/resources/medication/valueset/ directory holds further value sets for other medication resources (route, body site, administration method, additional instruction, as-needed reason, medication-not-given reason). MedicationStatement binds only medication → system-medication.

PeriodSpec shape​

effective_period is stored as JSON but typed as PeriodSpec (from care/emr/resources/base.py).

FieldTypeNotes
startdatetime | NoneMust be timezone-aware if present; naive datetimes are rejected
enddatetime | NoneMust be timezone-aware if present

validate_period (mode after) rejects a naive start/end and requires start to be no greater than end.

Resource specs (API schema)​

Every spec subclasses EMRResource (care/emr/resources/base.py), which provides serialize (DB object → pydantic) and de_serialize (pydantic → DB object). __model__ = MedicationStatement, and __exclude__ = ["patient", "encounter"] holds those two fields out of the generic field copy so the extra hooks set them instead.

Spec classRoleFields exposed
BaseMedicationStatementSpecshared baseid, status, reason, medication, dosage_text, effective_period, encounter, information_source, note
MedicationStatementSpecwrite · createInherits BaseMedicationStatementSpec; adds encounter existence validation + extra deserialization
MedicationStatementUpdateSpecwrite · updatestatus, effective_period, note only
MedicationStatementReadSpecread · list & detailInherits base; adds created_by, updated_by (UserSpec), created_date, modified_date

What the specs validate and maintain on your behalf:

  • encounter must exist — MedicationStatementSpec.validate_encounter_exists, a field_validator on encounter, raises "Encounter not found" unless an Encounter with that external_id exists.
  • patient is derived server-side — MedicationStatementSpec.perform_extra_deserialization runs only on create (is_update false): it resolves the Encounter from the supplied UUID and sets obj.patient = obj.encounter.patient. Clients never send patient.
  • Updates are narrow — MedicationStatementUpdateSpec accepts only status, effective_period, and note. medication, reason, dosage_text, information_source, and encounter can't be changed through it.
  • Read serialization — MedicationStatementReadSpec.perform_extra_serialization sets id = obj.external_id, encounter = obj.encounter.external_id, and serializes audit users via serialize_audit_users (created_by/updated_by as UserSpec).
  • medication value-set check — runs at validation time against Coding.code (see Bound value set).
  • effective_period tz-awareness and ordering — enforced by PeriodSpec (see PeriodSpec shape).

There is no status_history. status is a single current value, not an appended log.

Methods & save behaviour​

  • From EMRBaseModel: external_id (UUID), audit fields, soft delete. Deletes are soft — a removed statement stays in the table with deleted=True and is hidden by the default manager.
  • Create flow: client sends MedicationStatementSpec → de_serialize copies scalar/JSON fields → perform_extra_deserialization resolves encounter and back-fills patient → model saved.
  • Read flow: MedicationStatementReadSpec.serialize(obj) copies fields, then perform_extra_serialization injects id, encounter (as UUIDs), and audit users.

API integration notes​

  • Exposed through Care's REST API, aligned with FHIR MedicationStatement. Use the spec field names above for payloads.
  • medication is a single SNOMED CT Coding object whose code must be in the system-medication value set — not a free CodeableConcept, and not a scalar string.
  • effective_period datetimes must be timezone-aware and ordered (start ≤ end).
  • encounter goes in as a UUID; patient is derived from it server-side and must not be sent.
  • Updates flow through MedicationStatementUpdateSpec and can touch only status, effective_period, and note.
  • information_source is what separates a self-reported or third-party statement from an actively managed prescription (MedicationRequest).
  • external_id (UUID), created_by/updated_by, created_date/modified_date, history, and meta are platform-maintained — don't set them from clients.