Skip to main content
Version: 3.1

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 medicationsystem-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 existMedicationStatementSpec.validate_encounter_exists, a field_validator on encounter, raises "Encounter not found" unless an Encounter with that external_id exists.
  • patient is derived server-sideMedicationStatementSpec.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 narrowMedicationStatementUpdateSpec accepts only status, effective_period, and note. medication, reason, dosage_text, information_source, and encounter can't be changed through it.
  • Read serializationMedicationStatementReadSpec.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 MedicationStatementSpecde_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.