Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

References/Medication Request

From OHC Network Wiki
referencemedicationsCARE 3.0+

A MedicationRequest records a prescriber's intent to supply or administer a medication to a patient — the FHIR [https://hl7.org/fhir/medicationrequest.html 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

Model Purpose
MedicationRequestPrescription Groups medication requests authored together for an encounter (a prescription)
MedicationRequest A 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

Field Type Required Notes
status CharField(100) → MedicationRequestStatus Yes (spec)
status_reason CharField(100) → StatusReason No
intent CharField(100) → MedicationRequestIntent Yes (spec)
category CharField(100) → MedicationRequestCategory Yes (spec)
priority CharField(100) → MedicationRequestPriority Yes (spec)
do_not_perform BooleanField → bool Yes
dispense_status CharField(100) → MedicationRequestDispenseStatus No

Medication & dosage

Field Type Required Notes
medication JSONField → ValueSetBoundCoding[system-medication] Conditional
method JSONField n/a
dosage_instruction JSONField → list[DosageInstruction] Yes
note TextField → str No

Subject & provenance

Field Type Required Notes
patient FK → Patient server-set
encounter FK → Encounter Yes (create)
authored_on DateTimeField → datetime Yes (spec)
requester FK → users.User No
requested_product FK → ProductKnowledge Conditional
prescription FK → MedicationRequestPrescription No

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

Value Meaning (FHIR)
altchoice Alternative choice
clarif Prescription requires clarification
drughigh Drug level too high
hospadm Admission to hospital
labint Lab interference issues
non_avail Patient not available
preg Patient is pregnant or breastfeeding
salg Allergy
sddi Drug interaction
sdupther Duplicate therapy
sintol Suspected intolerance
surg Patient scheduled for surgery
washout Washout

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.

Value Unit
s second
min minute
h hour
d day
wk week
mo month
a year

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.

Field Type Required Notes
sequence int No
text str No
additional_instruction list[Coding] No
patient_instruction str No
timing Timing No
as_needed_boolean bool Yes
as_needed_for Coding No
site Coding No
route Coding No
method Coding No
dose_and_rate DoseAndRate No
max_dose_per_period DoseRange No

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).

Field Value set slug SNOMED CT scope
medication system-medication << 763158003 Medicinal product
dosage_instruction[].route system-route is-a 284009009 Route of administration
dosage_instruction[].site system-body-site is-a 91723000 Anatomical structure
dosage_instruction[].method system-administration-method is-a 736665006
dosage_instruction[].as_needed_for system-as-needed-reason is-a 404684003 Clinical finding
dosage_instruction[].additional_instruction system-additional-instruction is-a 419492006

system-medication-not-given lives in this directory too, but it binds on References/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 class Role Notes
MedicationRequestAbstractSpec shared (fields) Plain BaseModel holding the clinical fields: status, status_reason, intent, category, priority, do_not_perform, medication, dosage_instruction, authored_on, note, dispense_status
MedicationRequestResource shared (binding) Binds the spec to the MedicationRequest model and declares __exclude__
BaseMedicationRequestSpec shared MedicationRequestResource + MedicationRequestAbstractSpec + id: UUID4
MedicationRequestSpec write · create Adds encounter (UUID, required), requester, requested_product, prescription (UUIDs), and create_prescription. Carries the validators and perform_extra_deserialization
MedicationRequestUpdateSpec write · update Narrow — only status, note, dispense_status are mutable after create
MedicationRequestReadWithoutPrescriptionSpec read · embedded Full read minus prescription; expands requested_product (ProductKnowledgeReadSpec), requester/created_by/updated_by (UserSpec), plus created_date/modified_date
MedicationRequestReadSpec read · detail/list Adds expanded prescription (MedicationRequestPrescriptionReadSpec) and the encounter external id
CreatePrescription write · 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 class Role Notes
BaseMedicationRequestPrescriptionSpec shared id, status (MedicationRequestPrescriptionStatus), note, name
MedicationRequestPrescriptionWriteSpec write · create Adds encounter (UUID), prescribed_by (UUID); resolves encounterpatient and prescribed_by server-side
MedicationRequestPrescriptionUpdateSpec write · update Same shared fields, no new FKs
MedicationRequestPrescriptionReadSpec read · list Adds created_date, modified_date, expanded prescribed_by (UserSpec), tags (rendered via SingleFacilityTagManager)
MedicationRequestPrescriptionRetrieveSpec read · detail Adds audit created_by/updated_by
MedicationRequestPrescriptionRetrieveMedicationsSpec read · detail+children Adds medications (each via MedicationRequestReadWithoutPrescriptionSpec) and full encounter (EncounterRetrieveSpec)
MedicationRequestPrescriptionRetrieveDetailedSpec read · detail Adds 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.

Pages that link here: