Skip to main content
Version: 3.0

Medication Request

A MedicationRequest records a prescriber's intent to supply or administer a medication to a patient — the FHIR MedicationRequest resource. You create one whenever a drug is ordered during an encounter; it carries the medication, dosage instructions, timing, and route as structured JSON, grouped under a prescription.

Source:

Several fields are opaque JSONFields whose structure the Django model never declares. The contract lives in the Pydantic resource specs under care/emr/resources/medication/request/: the enums, the nested shape of those JSON fields, validation rules, the read/write schema split, and the server-side side effects. The tables below merge both layers.

Models​

ModelPurpose
MedicationRequestPrescriptionGroups medication requests authored together for an encounter (a prescription)
MedicationRequestA single medication order/request for a patient within an encounter

Both extend EMRBaseModel, which supplies external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON.

MedicationRequest fields​

Order status & intent​

FieldTypeRequiredNotes
statusCharField(100) → MedicationRequestStatusYes (spec)Nullable on the model, required by the spec; defaults to active. See status values
status_reasonCharField(100) → StatusReason | NoneNoWhy the request was discontinued or changed. See status reason values
intentCharField(100) → MedicationRequestIntentYes (spec)Nullable on the model, required by the spec. See intent values
categoryCharField(100) → MedicationRequestCategoryYes (spec)Nullable on the model, required by the spec. See category values
priorityCharField(100) → MedicationRequestPriorityYes (spec)Nullable on the model, required by the spec. See priority values
do_not_performBooleanField → boolYesSet True to record that the medication must explicitly not be given
dispense_statusCharField(100) → MedicationRequestDispenseStatus | NoneNoDefaults to None. Tracks dispense progress. See dispense status values

Medication & dosage​

FieldTypeRequiredNotes
medicationJSONField → ValueSetBoundCoding[system-medication] | NoneConditionalModel default {}. A single Coding { system, code, display } bound to the Medication value set (SNOMED CT medicinal products). Set exactly one of medication / requested_product
methodJSONFieldn/aModel default {}. Stored but not exposed by any current spec — administration method travels per dosage line via dosage_instruction[].method
dosage_instructionJSONField → list[DosageInstruction]YesModel default []. The structured dosage lines. See DosageInstruction shape
noteTextField → str | NoneNoFree-text note

Subject & provenance​

FieldTypeRequiredNotes
patientFK → Patientserver-setCASCADE. Ignored if sent by the client — derived from encounter.patient
encounterFK → EncounterYes (create)CASCADE. Write spec takes encounter as a UUID and validates that it exists
authored_onDateTimeField → datetimeYes (spec)Must be tz-aware. Model default timezone.now when omitted
requesterFK → users.UserNoSET_NULL. The prescriber; write spec takes a requester UUID
requested_productFK → ProductKnowledgeConditionalSET_NULL, default None. Write spec takes a requested_product UUID. Set exactly one of medication / requested_product
prescriptionFK → MedicationRequestPrescriptionNoSET_NULL, default None. Set via a prescription UUID or create_prescription (see validation)

Enum values​

Spec enums use underscored member values (on_hold, entered_in_error, original_order), not the hyphenated FHIR spellings. These are the exact strings the API accepts and returns.

MedicationRequestStatus values​

Value
active
on_hold
ended
stopped
completed
cancelled
entered_in_error
draft
unknown

StatusReason values​

ValueMeaning (FHIR)
altchoiceAlternative choice
clarifPrescription requires clarification
drughighDrug level too high
hospadmAdmission to hospital
labintLab interference issues
non_availPatient not available
pregPatient is pregnant or breastfeeding
salgAllergy
sddiDrug interaction
sduptherDuplicate therapy
sintolSuspected intolerance
surgPatient scheduled for surgery
washoutWashout

MedicationRequestIntent values​

Value
proposal
plan
order
original_order
reflex_order
filler_order
instance_order

MedicationRequestPriority values​

Value
routine
urgent
asap
stat

MedicationRequestCategory values​

Value
inpatient
outpatient
community
discharge

MedicationRequestDispenseStatus values​

Value
complete
partial
incomplete
declined

TimingUnit values​

UCUM time units used by Timing.repeat.period_unit and bounds_duration.unit.

ValueUnit
ssecond
minminute
hhour
dday
wkweek
momonth
ayear

DoseType values​

Used by DoseAndRate.type.

Value
ordered
calculated

DosageInstruction nested spec​

Each entry in the dosage_instruction JSON list deserializes to a DosageInstruction (Pydantic, snake_case keys) — the structure hiding behind the opaque JSONField.

FieldTypeRequiredNotes
sequenceint | NoneNoOrder of the instruction
textstr | NoneNoFree-text dosage instruction (SIG)
additional_instructionlist[Coding] | NoneNoBound to the Additional Instruction value set
patient_instructionstr | NoneNoPatient-facing instructions
timingTiming | NoneNoSee Timing below
as_needed_booleanboolYesSet True for PRN orders
as_needed_forCoding | NoneNoReason for PRN; bound to the As Needed value set
siteCoding | NoneNoBound to the Body Site value set
routeCoding | NoneNoBound to the Route value set
methodCoding | NoneNoBound to the Administration Method value set
dose_and_rateDoseAndRate | NoneNoSee DoseAndRate below
max_dose_per_periodDoseRange | NoneNoUpper dose limit per period

Sub-types under DosageInstruction​

Timing {
repeat: TimingRepeat # required
code: Coding | None # e.g. BID/TID/QID timing abbreviation
}

TimingRepeat {
frequency: int # required — repetitions per period
period: Decimal(20,0) # required — duration the frequency applies to
period_unit: TimingUnit # required — s|min|h|d|wk|mo|a
bounds_duration: TimingQuantity # required — total span
}

TimingQuantity { # integer-valued time amount
value: Decimal(20,0) # required
unit: TimingUnit # required
}

DoseAndRate {
type: DoseType # required — ordered | calculated
dose_range: DoseRange | None # for titrated doses
dose_quantity: DosageQuantity | None # for regular doses
}

DoseRange {
low: DosageQuantity # required
high: DosageQuantity # required
}

DosageQuantity { # measured dose amount
value: Decimal(20,6) # required
unit: Coding # required
}

The Pydantic specs define these JSON keys as snake_case (as_needed_boolean, period_unit, dose_and_rate, max_dose_per_period, bounds_duration), so that is how they sit in storage — not the camelCase FHIR wire format.

Bound value sets​

Coded fields bind to Care value sets through ValueSetBoundCoding[<slug>]. On write, the supplied Coding.code is validated against the value set; a code outside it is rejected. All draw from SNOMED CT (http://snomed.info/sct).

FieldValue set slugSNOMED CT scope
medicationsystem-medication<< 763158003 Medicinal product
dosage_instruction[].routesystem-routeis-a 284009009 Route of administration
dosage_instruction[].sitesystem-body-siteis-a 91723000 Anatomical structure
dosage_instruction[].methodsystem-administration-methodis-a 736665006
dosage_instruction[].as_needed_forsystem-as-needed-reasonis-a 404684003 Clinical finding
dosage_instruction[].additional_instructionsystem-additional-instructionis-a 419492006

system-medication-not-given lives in this directory too, but it binds on Medication Administration, not on the request.

Resource specs (API schema)​

Every spec extends EMRResource, which provides serialize / de_serialize plus the perform_extra_serialization / perform_extra_deserialization hooks. MedicationRequestResource sets __exclude__ = [patient, encounter, requester, requested_product, prescription], so those relations are wired up by hand instead of auto-mapped.

Spec classRoleNotes
MedicationRequestAbstractSpecshared (fields)Plain BaseModel holding the clinical fields: status, status_reason, intent, category, priority, do_not_perform, medication, dosage_instruction, authored_on, note, dispense_status
MedicationRequestResourceshared (binding)Binds the spec to the MedicationRequest model and declares __exclude__
BaseMedicationRequestSpecsharedMedicationRequestResource + MedicationRequestAbstractSpec + id: UUID4
MedicationRequestSpecwrite · createAdds encounter (UUID, required), requester, requested_product, prescription (UUIDs), and create_prescription. Carries the validators and perform_extra_deserialization
MedicationRequestUpdateSpecwrite · updateNarrow — only status, note, dispense_status are mutable after create
MedicationRequestReadWithoutPrescriptionSpecread · embeddedFull read minus prescription; expands requested_product (ProductKnowledgeReadSpec), requester/created_by/updated_by (UserSpec), plus created_date/modified_date
MedicationRequestReadSpecread · detail/listAdds expanded prescription (MedicationRequestPrescriptionReadSpec) and the encounter external id
CreatePrescriptionwrite · nested{ name?, note?, alternate_identifier (required) } — inline prescription creation

Validation (create — MedicationRequestSpec)​

  • Medication XOR product: you cannot set both medication and requested_product, and one is required.
  • Prescription XOR create: you cannot set both prescription and create_prescription.
  • Encounter existence: the encounter UUID must match an existing Encounter (field validator).
  • authored_on, and any nested PeriodSpec/timing datetimes, must be timezone-aware.

Server-side side effects (perform_extra_deserialization)​

  • encounter is resolved from its UUID, then patient is copied from encounter.patient — the client never sets patient directly.
  • requester / requested_product are resolved by external_id. If requested_product.facility is set and differs from encounter.facility, the write raises "Product not found in facility".
  • prescription is resolved by external_id, scoped to the same encounter.
  • create_prescription looks up an existing prescription by (alternate_identifier, encounter). A match that is not active raises "Prescription is not active"; no match creates a new MedicationRequestPrescription (status=active, copying name/note, prescribed_by=requester) and links the request to it.

Serialization (read)​

perform_extra_serialization sets id = external_id and expands requested_product, requester, and the audit users (created_by/updated_by) from cache. MedicationRequestReadSpec goes further, expanding prescription and emitting encounter as its external id.

MedicationRequestPrescription​

The container that groups every medication request authored together for an encounter into one prescription. Each request points back via MedicationRequest.prescription.

encounter → FK Encounter (CASCADE)
patient → FK Patient (CASCADE)
name → CharField(100), nullable
note → TextField, nullable
prescribed_by → FK users.User (CASCADE, nullable)
status → CharField(100), nullable → MedicationRequestPrescriptionStatus
approval_status → CharField(100), nullable
alternate_identifier → CharField(100), nullable
tags → ArrayField[int], default []

A UniqueConstraint (unique_alternate_identifier_encounter) keeps alternate_identifier unique per encounter.

MedicationRequestPrescriptionStatus values​

active, on_hold, ended, stopped, completed, cancelled, entered_in_error, draft.

A pharmacist may only move a prescription between active, on_hold, completed (MEDICATION_PRESCRIPTION_PHARMACIST_ALLOWED_STATUS).

Prescription specs​

Spec classRoleNotes
BaseMedicationRequestPrescriptionSpecsharedid, status (MedicationRequestPrescriptionStatus), note, name
MedicationRequestPrescriptionWriteSpecwrite · createAdds encounter (UUID), prescribed_by (UUID); resolves encounter→patient and prescribed_by server-side
MedicationRequestPrescriptionUpdateSpecwrite · updateSame shared fields, no new FKs
MedicationRequestPrescriptionReadSpecread · listAdds created_date, modified_date, expanded prescribed_by (UserSpec), tags (rendered via SingleFacilityTagManager)
MedicationRequestPrescriptionRetrieveSpecread · detailAdds audit created_by/updated_by
MedicationRequestPrescriptionRetrieveMedicationsSpecread · detail+childrenAdds medications (each via MedicationRequestReadWithoutPrescriptionSpec) and full encounter (EncounterRetrieveSpec)
MedicationRequestPrescriptionRetrieveDetailedSpecread · detailAdds encounter via EncounterListSpec

Methods & save behaviour​

  • Read and write run through the inherited EMRResource.serialize / de_serialize. de_serialize dumps with exclude_defaults=True, maps the remaining fields onto the model, then calls perform_extra_deserialization for the relation and prescription handling above.
  • patient always comes from encounter; the client never writes it.
  • Inline prescription creation (create_prescription) is idempotent on (alternate_identifier, encounter) and proceeds only when an existing match is active.
  • authored_on falls back to timezone.now at the model layer when omitted.

API integration notes​

  • Coded fields take a single Coding object { system, code, display } validated against bound SNOMED CT value sets — send a code from the right value set or the write fails.
  • Enum members are underscored (on_hold, entered_in_error, original_order); send those literals, not the FHIR hyphenated forms.
  • dosage_instruction carries SIG text, timing/frequency, PRN flags, dose and rate, and max-dose limits — write the full nested shape (snake_case keys) rather than free text alone.
  • On create, send either medication or requested_product (not both) and at most one of prescription / create_prescription.
  • Updates reach only status, note, and dispense_status.
  • requester, requested_product, and prescription use SET_NULL, so a request outlives the user, product, or prescription it referenced.