Location
FacilityLocation is a node in a facility's building/ward/room/bed tree — where resources sit and where care happens. You work with it when you place an encounter somewhere, scope organization access to part of a facility, or model a site's physical layout.
Source:
- Model:
care/emr/models/location.py - Resource spec:
care/emr/resources/location/spec.py - Base resource:
care/emr/resources/base.py - Coding type:
care/emr/resources/common/coding.py
Nodes nest without limit: every node may have a parent and children, so one tree can run from a campus down to a single bed. Encounters and organizations attach to nodes, and access granted at a node cascades to its descendants asynchronously rather than resolving on each request. The model maps to the FHIR Location resource.
The Django model is only the storage layer. location_type, metadata, and cached_parent_json are opaque JSONFields; their real shape lives in the Pydantic resource specs (care/emr/resources/location/spec.py), which also define every enum and the read/write schema split. See Resource specs (API schema).
Models
| Model | Purpose |
|---|---|
FacilityLocation | A node in a facility's location tree (building, ward, room, bed, …) |
FacilityLocationOrganization | Grants a FacilityOrganization access to a location |
FacilityLocationEncounter | Records how/when an encounter occupied a location (e.g. a bed) |
All three extend EMRBaseModel, which supplies external_id, audit fields, and soft-delete semantics.
FacilityLocation fields
Descriptive
| Field | Type | Spec values / shape | Notes |
|---|---|---|---|
name | CharField(255) | str, required | Must be unique among siblings at the same level under the same root (see validation) |
description | CharField(255) | str, required | Free-text |
status | CharField(255) | StatusChoices enum | One of StatusChoices |
operational_status | CharField(255) | FacilityLocationOperationalStatusChoices enum | FHIR operational status code; one of FacilityLocationOperationalStatusChoices |
system_availability_status | CharField(255) | str (read-only) | Server-maintained, derived from encounter association. Resolves to one of LocationAvailabilityStatusChoices (available / reserved) |
mode | CharField(255) | FacilityLocationModeChoices enum | kind (a type, e.g. a building/ward) or instance (a single location, e.g. Bed 1). Set on create, then frozen. See FacilityLocationModeChoices |
location_type | JSONField (default=dict, nullable) | Coding | None (default None) | A single FHIR Coding, not a CodeableConcept. Unbound — any coding is accepted |
form | CharField(255) | FacilityLocationFormChoices enum | FHIR physical form; one of FacilityLocationFormChoices |
Hierarchy & tree caches
These fields denormalize the tree so descendants are queryable without recursive joins. save() and the cascade task own them; never set them from a client.
| Field | Type | Notes |
|---|---|---|
parent | FK → FacilityLocation | SET_NULL, nullable. Immediate parent. Accepted on write as a UUID (parent); on read it's dropped in favour of the serialized parent JSON |
root_location | FK → self | CASCADE, related_name="root", nullable. Top of this node's tree. Excluded from specs |
has_children | BooleanField | default=False. Set True on the parent when a child is created. Read-only in list/retrieve specs |
level_cache | IntegerField | default=0. Depth in the tree (parent.level_cache + 1) |
parent_cache | ArrayField[int] | Ordered ancestor IDs (parent.parent_cache + [parent.id]) |
cached_parent_json | JSONField (default=dict) | Serialized parent chain (FacilityLocationListSpec) plus a cache_expiry ISO timestamp. Rebuilt lazily by get_parent_json(), invalidated by the cascade task. cache_expiry_days = 15 |
sort_index | IntegerField | default=0. In specs int | None, default 0, constrained 0 ≤ sort_index ≤ 10000 (MIN_SORT_INDEX/MAX_SORT_INDEX). Auto-assigned as max(sibling sort_index) + 1 when left unset |
Access & encounter
| Field | Type | Notes |
|---|---|---|
facility | FK → facility.Facility | PROTECT. Owning facility. Excluded from specs — resolved from route/context |
facility_organization_cache | ArrayField[int] | default=list. Org IDs that can reach this location; rebuilt by sync_organization_cache() |
current_encounter | FK → Encounter | SET_NULL, nullable, default=None. Populated from FacilityLocationEncounter. Excluded from the base spec; surfaced as serialized JSON in list/retrieve specs |
metadata | JSONField | default=dict. Open extension bag for deployment-specific data |
Enum values
Every enum is a str Enum in care/emr/resources/location/spec.py. Values below are character-for-character from source.
StatusChoices values
| Value | Meaning |
|---|---|
active | Location is active |
inactive | Location is inactive |
unknown | Status unknown |
FacilityLocationOperationalStatusChoices values
FHIR HL7 v2 bed/location operational status codes.
| Code | Meaning |
|---|---|
C | Closed |
H | Housekeeping |
O | Occupied |
U | Unoccupied |
K | Contaminated |
I | Isolated |
FacilityLocationModeChoices values
| Value | Meaning |
|---|---|
instance | A single, concrete location (e.g. Bed 1). Instances cannot have children |
kind | A class/type of location (e.g. a ward or building) |
FacilityLocationFormChoices values
FHIR location-physical-type codes.
| Code | Meaning |
|---|---|
si | Site |
bu | Building |
wi | Wing |
wa | Ward |
lvl | Level |
co | Corridor |
ro | Room |
bd | Bed |
ve | Vehicle |
ho | House |
ca | Cabinet |
rd | Road |
area | Area |
jdn | Jurisdiction |
vi | Virtual |
LocationEncounterAvailabilityStatusChoices values
Used by FacilityLocationEncounter.status — how an encounter occupies a location.
| Value | Meaning |
|---|---|
planned | Occupancy planned |
active | Currently occupied |
reserved | Reserved |
completed | Occupancy ended |
LocationAvailabilityStatusChoices values
The server-derived values that system_availability_status resolves to.
| Value | Meaning |
|---|---|
available | Not actively tied to an encounter |
reserved | Tied to an encounter |
Coding shape
location_type is a single Coding object (extra="forbid"):
Coding {
system: str | None = None
version: str | None = None
code: str # required
display: str | None = None
}
Related models
FacilityLocationOrganization
Links an organization to a location. Users under that organization inherit access to the location and its encounters through whatever role they hold on the organization.
location → FK FacilityLocation (CASCADE)
organization → FK FacilityOrganization (CASCADE)
Saving one of these rows calls location.save() (rebuilding facility_organization_cache), then location.cascade_changes() to propagate the grant down to descendants asynchronously. On write, the organization set comes from FacilityLocationWriteSpec.organizations, a list of UUIDs.
FacilityLocationEncounter
A bed assignment, in effect: it records how an encounter occupied a location over a time window.
| Field | Type | Spec | Notes |
|---|---|---|---|
status | CharField(25) | LocationEncounterAvailabilityStatusChoices | One of planned / active / reserved / completed |
location | FK → FacilityLocation | excluded / nested | CASCADE. Surfaced as JSON in ...ListSpecWithLocation |
encounter | FK → Encounter | UUID on write, JSON on read | CASCADE. Validated to exist on create |
start_datetime | DateTimeField | datetime (required) | Occupancy start |
end_datetime | DateTimeField (nullable) | datetime | None | Occupancy end; open-ended when null |
FacilityLocation.current_encounter is populated from these rows.
Methods & save behaviour
save() side effects
On insert (no id yet):
- If
parentis set,level_cache,root_location, andparent_cacheare derived from the parent, and the parent'shas_childrenis flipped toTrueif needed. - If
sort_indexis unset, it's assignedmax(sibling sort_index) + 1.
On update, cached_parent_json is cleared so it rebuilds lazily. After every super().save(), sync_organization_cache() runs and persists facility_organization_cache through a second save(update_fields=[...]), so each write touches the row twice.
Caches & cascade
sync_organization_cache()— unions the parent's org cache, the org chains of every linkedFacilityLocationOrganization, and the facility'sdefault_internal_organization_id, then writesfacility_organization_cache.get_parent_json()— returns the cached parent chain, refreshingcached_parent_json(viaFacilityLocationListSpec.serialize(parent)) once it expires (cache_expiry_days = 15).cascade_changes()/handle_cascade(base_location)— a Celery task that walks every descendant and re-saves it to invalidate each node'scached_parent_json. It fires when a location's organizations change, so propagation is eventually consistent, not immediate.
validate_uniqueness()
Classmethod that enforces name uniqueness among siblings at the same level_cache within the same root_location — or among root-level locations when there's no root. For a new instance with a parent, level_cache and root_location are computed from that parent before the check runs.
Resource specs (API schema)
The Pydantic specs in care/emr/resources/location/spec.py define the request/response shapes. All extend EMRResource, which provides serialize/de_serialize plus the perform_extra_serialization/perform_extra_deserialization hooks (see base model and base.py).
Location specs
| Spec class | Role | Exposes / notes |
|---|---|---|
FacilityLocationBaseSpec | shared base | __model__ = FacilityLocation; id: UUID4 | None. __exclude__ = [parent, facility, organizations, root_location, current_encounter] |
FacilityLocationSpec | shared fields | status, operational_status, name, description, location_type (Coding | None), form, sort_index (0..10000, default 0) |
FacilityLocationWriteSpec | write · create | Adds parent: UUID4 | None, organizations: list[UUID4] (required), mode: FacilityLocationModeChoices. Validator: a set parent must exist and must not be mode = instance ("Instances cannot have children"). perform_extra_deserialization resolves the parent UUID to an FK |
FacilityLocationUpdateSpec | write · update | Same fields as FacilityLocationSpec. Drops parent/organizations/mode — hierarchy and mode are fixed after creation |
FacilityLocationMinimalListSpec | read · minimal | Adds parent: dict, mode: str, has_children: bool, system_availability_status: str. perform_extra_serialization sets id = external_id and parent = obj.get_parent_json() |
FacilityLocationListSpec | read · list | Extends minimal; adds current_encounter: dict | None, serialized via EncounterListSpec when present |
FacilityLocationRetrieveSpec | read · detail | Extends list; adds created_by/updated_by (via serialize_audit_users) |
Encounter-association specs
| Spec class | Role | Exposes / notes |
|---|---|---|
FacilityLocationEncounterBaseSpec | shared base | __model__ = FacilityLocationEncounter; id: UUID4 | None. __exclude__ = [encounter, location] |
FacilityLocationEncounterCreateSpec | write · create | status (LocationEncounterAvailabilityStatusChoices), encounter: UUID4, start_datetime, end_datetime: datetime | None. Validator: encounter must exist. perform_extra_deserialization resolves the encounter UUID to an FK |
FacilityLocationEncounterUpdateSpec | write · update | status, start_datetime, end_datetime. Encounter is not re-assignable |
FacilityLocationEncounterListSpec | read · list | encounter: UUID4, start_datetime, end_datetime, status: str; id = external_id |
FacilityLocationEncounterListSpecWithLocation | read · list | Extends list; adds location: dict (serialized via FacilityLocationListSpec) |
FacilityLocationEncounterReadSpec | read · detail | encounter: dict (serialized via EncounterRetrieveSpec), start_datetime, end_datetime, status, created_by/updated_by |
Server-maintained behaviour
- These are platform-maintained — don't write them from a client:
system_availability_status,has_children,level_cache,parent_cache,cached_parent_json,facility_organization_cache,current_encounter, andsort_index(when left unset). - On read,
parentcomes back as the serialized parent chain (get_parent_json()), never the raw FK. - Access granted through
organizations/FacilityLocationOrganizationlands asynchronously; the org cache and descendant caches settle only after the cascade task runs. location_typetakes a freeCoding(no value-set slug) andmetadatais an open bag, so both add coded or extension data without a schema migration.
API integration notes
- The API is modelled on the FHIR
Locationresource, so payload field names may differ from these Django names. modeseparateskind(a location type) frominstance(a concrete location such as a bed). Beds areFacilityLocationinstances, not a separate model, andinstancelocations cannot have children.parent,organizations, andmodeare settable only at creation (FacilityLocationWriteSpec). Updates go throughFacilityLocationUpdateSpec, which omits all three.- For fields you can't write, see Server-maintained behaviour.
Related
- Reference: Facility
- Reference: Organization
- Reference: Encounter
- Reference: Base model
- Source: location.py on GitHub
- Source: location/spec.py on GitHub