Skip to main content
Version: 3.1

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_performBooleanFieldboolYesSet 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
medicationJSONFieldValueSetBoundCoding[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_instructionJSONFieldlist[DosageInstruction]YesModel default []. The structured dosage lines. See DosageInstruction shape
noteTextFieldstr | 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_onDateTimeFielddatetimeYes (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 encounterpatient 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.