Skip to main content
Version: 3.0

Value Set

A ValueSet is a FHIR-aligned composition of coded concepts drawn from one or more code systems. Reach for one when a questionnaire needs to constrain a coded answer to a fixed set of options.

The Django model is only storage — compose is an opaque JSONField. The structure that matters (include/exclude clauses, filters, the status enum, validation, and the read/write API schemas) lives in the Pydantic resource specs, covered alongside the model here.

Source:

Models

ModelPurpose
ValueSetA named, FHIR-compatible composition of codes from one or more code systems
UserValueSetPreferenceStores a user's favorited codes within a value set
RecentViewsManagerRedis-backed helper for tracking a user's recently used codes (not a database model)

ValueSet and UserValueSetPreference extend EMRBaseModel, the shared Care EMR base that supplies external_id, audit fields, history/meta JSON, and soft-delete semantics.

ValueSet fields

A value set stores rules, not a flat list of codes: a compose object of include/exclude clauses against code systems. Members resolve in real time against terminology servers when the value set is searched or a code is looked up.

FieldModel typeSpec typeRequiredDefaultNotes
slugSlugField(255)SlugTypeyesunique, indexed. Public identifier. Spec constrains to min 5 / max 50 chars, URL-safe (^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$); must start and end alphanumeric. Uniqueness re-checked in spec; system- prefix reserved (see validation)
nameCharField(255)stryesDisplay name. Spec rejects blank/whitespace-only and strips surrounding whitespace
descriptionTextFieldstryes (in spec)"" (model)Free text. The model defaults to an empty string; the spec requires the field present
composeJSONFieldValueSetComposeyesdictComposition rules — structured include/exclude clauses (see compose shape). On write, persisted via compose.model_dump(exclude_defaults=True, exclude_none=True)
statusCharField(255)ValueSetStatusOptionsyesPublication status enum (see status values)
is_system_definedBooleanFieldboolnoFalseMarks value sets shipped and maintained by the platform rather than authored by a deployment

ValueSetStatusOptions values

A str enum in resources/valueset/spec.py. The model column is a free CharField; the spec restricts writes to these four values.

ValueMeaning
draftAuthored, not yet in use
activePublished and usable
retiredNo longer recommended for use
unknownStatus not known

compose shape

compose holds a ValueSetCompose structure (care.emr.resources.common.valueset). Each clause targets a system (a code system URI) and selects concepts directly or through filters. Every nested model sets extra="forbid", so unknown keys are rejected.

ValueSetCompose

FieldTypeRequiredDefaultNotes
idstr | NonenoNoneOptional identifier
includelist[ValueSetInclude]yesInclusion clauses; the field must be present
excludelist[ValueSetInclude] | NonenoNoneExclusion clauses; same shape as include
propertylist[str] | NonenoNoneProperties to return for member concepts

ValueSetInclude (used for both include and exclude)

FieldTypeRequiredDefaultNotes
idstr | NonenoNoneOptional identifier
systemstr | NonenoNoneCode system URI this clause targets
versionstr | NonenoNoneOptional code system version
conceptlist[ValueSetConcept] | NonenoNoneExplicitly pinned concepts
filterlist[ValueSetFilter] | NonenoNoneRule-based selection

A clause may set concept or filter, not both — enforced by check_concept_or_filter (mode=after).

ValueSetConcept

FieldTypeRequiredDefault
idstr | NonenoNone
codestr | NonenoNone
displaystr | NonenoNone

ValueSetFilter

FieldTypeRequiredDefaultNotes
idstr | NonenoNone
propertystr | NonenoNoneThe concept property to filter on
opstr | NonenoNoneFilter operator — validated against the allowed list below
valuestr | NonenoNoneValue to compare against
op allowed values

validate_op rejects anything outside this set: =, is-a, descendent-of, is-not-a, regex, in, not-in, generalizes, child-of, descendent-leaf, exists.

compose:
include:
- system: <code system uri>
version: <optional system version>
concept: [ { code, display } ] # pin concepts (mutually exclusive with filter)
filter: [ { property, op, value } ] # rule-based selection
exclude:
- ... same ValueSetInclude shape ...
property: [ <property names to return> ]

create_composition() regroups these clauses by system into { <system>: {include: [...], exclude: [...]} } so each system can be queried independently, dumping each clause with exclude_defaults=True.

Note: a second, simpler ValueSet BaseModel (name, status, compose) plus ValueSetConcept/ValueSetFilter/ValueSetInclude/ValueSetCompose all live in resources/common/valueset.py. The persisted/API resource model is ValueSetBaseSpec in resources/valueset/spec.py, which reuses the same ValueSetCompose.

Resource specs (API schema)

Every spec extends EMRResource (resources/base.py), which provides serialize (DB object → pydantic, via model_construct) and de_serialize (pydantic → DB object). ValueSet leaves __store_metadata__ unset, so spec fields map directly to model columns — there is no meta bag.

Spec classRoleFields exposed
ValueSetBaseSpecshared base (__model__ = ValueSet)id (UUID4), slug, name, description, compose, status, is_system_defined
ValueSetSpecwrite · create & updateinherits base + validation + perform_extra_deserialization
ValueSetReadSpecread · list & detailinherits base + created_by, updated_by (dicts)

Validation (ValueSetSpec)

RuleWhereBehaviour
Name not emptyvalidate_name (field)Rejects blank/whitespace-only; strips surrounding whitespace
Slug uniquevalidate_slug (field)Queries existing ValueSet rows; on update, excludes the current object (via context["object"].id when context["is_update"]); otherwise raises "Slug must be unique"
Reserved slugvalidate_slug_system (model, after)If the value set is not is_system_defined and the slug contains "system-", raises "Cannot create valueset with system like slug"
Slug formatSlugTypemin 5 / max 50 chars, URL-safe, alphanumeric start/end
op allowlistValueSetFilter.validate_opSee op allowed values
concept xor filterValueSetInclude.check_concept_or_filterBoth present → error

Server-side serialization hooks

HookSpecEffect
perform_extra_deserialization(is_update, obj)ValueSetSpecOn write, sets obj.compose = self.compose.model_dump(exclude_defaults=True, exclude_none=True), normalizing the JSON stored in the model
perform_extra_serialization(mapping, obj)ValueSetReadSpecOn read, sets mapping["id"] = obj.external_id and populates created_by / updated_by via serialize_audit_users

ValueSetSpec.model_rebuild() runs at module load so the forward reference to ValueSetCompose resolves.

UserValueSetPreference

Holds the codes a single user has favorited within one value set.

user → FK users.User (CASCADE)
valueset → FK emr.ValueSet (CASCADE)
favorite_codes → JSONField (default=list)

unique_together = ("user", "valueset") enforces one preference row per user per value set. The class constant MAX_FAVORITES (default 50, overridable via settings.MAX_FAVORITES_FOR_VALUESET) caps how many codes a user may favorite.

RecentViewsManager

A classmethod-only helper, not a database model. It tracks a user's recently viewed codes in a Redis list (one per cache_key) so pickers can surface recent codes alongside favorites.

MemberBehaviour
get_client()Lazily opens the "default" Redis connection
get_recent_views(cache_key)Returns the decoded list of recent code objects
add_recent_view(cache_key, code_obj)De-dupes by code (no-op if code missing), LPUSHes the entry, then LTRIMs to MAX_RECENT_VIEW
remove_recent_view(cache_key, code_obj)Removes any list entry matching code (no-op if code missing)
clear_recent_views(cache_key)Deletes the whole list
_remove_by_code(cache_key, code)Internal: scans the list and LREMs matching entries; malformed JSON entries are skipped

MAX_RECENT_VIEW defaults to 20 (overridable via settings.MAX_RECENT_VIEW_FOR_VALUESET). Entries are JSON-encoded; malformed ones are skipped on read and remove.

Methods & save behaviour

ValueSet does not override save()/delete() — soft-delete is inherited from the base. Its methods drive terminology resolution:

  • create_composition() — converts compose (a dict or ValueSetCompose) into a per-system map of include/exclude clauses, dumping each clause with exclude_defaults=True.
  • search(search="", count=10, display_language=None) — runs ValueSetResource().filter(...).search() for each system in the composition (optionally filtered by display_language) and concatenates the results. Codes are fetched from the terminology server at request time; nothing is materialized into the value set.
  • lookup(code) — checks whether code is a member by running ValueSetResource().filter(...).lookup(code) against each system, returning True if any system matches.

Membership resolves live, so editing compose immediately changes which codes the value set yields. There is no expansion or cache to rebuild.

API integration notes

  • The REST API aligns with the FHIR ValueSet resource. Writes go through ValueSetSpec, reads through ValueSetReadSpec; search powers code pickers and lookup validates submitted codes.
  • The model column compose is opaque JSON, so always write through the ValueSetCompose schema (clauses, filters, the op allowlist, concept xor filter). perform_extra_deserialization re-dumps it with exclude_defaults/exclude_none to keep stored JSON normalized.
  • Composition rules are stored, not expanded code lists. Members are queried in real time from terminology servers, so results shift as upstream systems change.
  • Treat is_system_defined value sets as read-only — they are platform-maintained. Author your own for local terminology; the system- slug prefix is reserved for system value sets.
  • favorite_codes (via UserValueSetPreference) and recent views (via RecentViewsManager/Redis) are per-user personalization, not part of the value set definition.