References/Tagging
More languages
More actions
A TagConfig defines one tag: its label, category, scope, and place in a tag tree. Tags are hierarchical labels you attach to resources — patients, encounters, charge items — to classify and filter them. You manage the definitions through TagConfig; the applied tags live as integer ID arrays on the tagged resources.
Two layers back the tag. The Django model (care/emr/models/tag_config.py) is storage — it holds opaque JSONFields (metadata, cached_parent_json) whose structure it never describes. The Pydantic resource specs (care/emr/resources/tag/config_spec.py) are the API and implementation layer: they pin down the enums, give those JSON fields a real shape, run field validation, and split read from write.
Source:
- Model: [https://github.com/ohcnetwork/care/blob/develop/care/emr/models/tag_config.py
care/emr/models/tag_config.py] - Resource spec: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/tag/config_spec.py
care/emr/resources/tag/config_spec.py] - Cache invalidation: [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/tag/cache_invalidation.py
care/emr/resources/tag/cache_invalidation.py]
Models
| Model | Purpose |
|---|---|
TagConfig
|
Definition of a single tag — its display, category, scope, status, metadata, and position in the tag tree |
TagConfig extends EMRBaseModel, the shared Care EMR base providing external_id, the audit fields created_by/updated_by and created_date/modified_date, soft-delete via deleted, and history/meta JSON. Applied tag IDs live on the tagged resource itself — for example Patient.instance_tags / Patient.facility_tags and Encounter.tags.
TagConfig fields
Scope
A tag is scoped to at most one owner — an instance-wide organization, a facility-scoped organization, or a facility.
| Field | Type | Notes |
|---|---|---|
facility |
FK → Facility |
PROTECT, nullable, default=None. Facility that owns the tag
|
facility_organization |
FK → FacilityOrganization |
CASCADE, nullable. Owning facility organization. Not allowed on instance-level (no-facility) tags
|
organization |
FK → Organization |
CASCADE, nullable. Owning instance-wide organization
{{Field|model=Tagging|section=Scope|name=resource|type=CharField(255)|notes=The resource type the tag applies to. Constrained by |
Display & classification
| Field | Type | Notes
{{Field|model=Tagging|section=Display & classification|name=status|type=CharField(255)|notes=Lifecycle status. Constrained by |
|---|---|---|
display |
CharField(255) |
Human-readable label. Required in the spec |
description |
TextField |
Nullable, blank-able. Required key in the base spec but accepts null
{{Field|model=Tagging|section=Display & classification|name=category|type=CharField(255)|notes=Grouping category. Constrained by |
priority |
IntegerField |
default=100. Ordering weight
{{Field|model=Tagging|section=Display & classification|name=metadata|type=JSONField|notes= |
Tree structure & caches
TagConfig is self-referential — tags form a tree, and denormalized caches are rebuilt on insert to avoid recursive joins (the same pattern as Organization). These fields are platform-maintained and never writable through the create/update specs.
| Field | Type | Notes |
|---|---|---|
parent |
FK → self |
CASCADE, nullable. related_name="children". Null parent means a root tag
|
root_tag_config |
FK → self |
CASCADE, nullable. related_name="root". Top of the tree, derived on save
|
has_children |
BooleanField |
default=False. Flipped to True on the parent when a child is created
|
level_cache |
IntegerField |
default=0. Depth in the tree (parent.level_cache + 1)
|
parent_cache |
ArrayField[int] |
default=list. Full ancestor id chain (parent.parent_cache + [parent.id])
|
cached_parent_json |
JSONField |
default=dict. Materialized parent record with a cache_expiry; rebuilt after cache_expiry_days (15). Shape: { id, display, description, category, parent (nested same shape), level_cache, cache_expiry }
|
Enums
TagCategoryChoices values
category binds to this enum in every spec (str values).
| Value |
|---|
diet
|
drug
|
lab
|
admin
|
contact
|
clinical
|
behavioral
|
research
|
advance_directive
|
safety
|
TagResource values
resource binds to this enum in TagConfigWriteSpec (set only on create; the read specs surface it as a plain str).
| Value |
|---|
encounter
|
activity_definition
|
service_request
|
charge_item
|
charge_item_definition
|
patient
|
token_booking
|
medication_request_prescription
|
supply_request_order
|
supply_delivery_order
|
account
|
TagStatus values
status binds to this enum in every spec (str values).
| Value | Meaning |
|---|---|
active
|
Tag is available for use |
archived
|
Tag retired; retained for historical references |
Nested specs (shape of JSON fields)
TagConfigMetadata (shape of metadata)
Gives the opaque metadata JSONField a real structure. A plain Pydantic BaseModel, not an EMRResource.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
color |
str |
no | ||
icon |
str |
no |
Resource specs (API schema)
Every spec extends EMRResource (care/emr/resources/base.py), which supplies serialize (DB object → spec) and de_serialize (spec → DB object) plus the perform_extra_serialization / perform_extra_deserialization hooks. __exclude__ on the base spec keeps the FK fields (facility, facility_organization, organization, parent) out of the generic field copy so each spec can resolve them explicitly.
| Spec class | Role | Notes |
|---|---|---|
TagConfigBaseSpec
|
shared | None), priority (int = 100), status (TagStatus), metadata (TagConfigMetadata?)
|
TagConfigWriteSpec
|
write · create | Base + facility?, facility_organization?, organization?, parent? (all UUID4), resource (TagResource, required). Runs two model_validator(after) checks and resolves FKs in perform_extra_deserialization
|
TagConfigUpdateSpec
|
write · update | Base + facility_organization?, organization? (UUID4). Cannot change facility, parent, or resource. Resolves organization/facility_organization in perform_extra_deserialization (facility organization scoped to obj.facility)
|
TagConfigReadSpec
|
read · list | None), resource (str), facility (dict | None). Inlines parent via get_parent_json() and a FacilityBareMinimumSpec for facility
|
TagConfigRetrieveSpec
|
read · detail | None), organization (dict | None). Adds audit users and full org/facility-org serialization
|
Write-spec validation (TagConfigWriteSpec)
Two @model_validator(mode="after") checks run before deserialization:
validate_exists:- If
facilityis set, it must exist; iffacility_organizationis also set it must belong to that facility, elseValidationError("Facility Organization not found"). - If
organizationis set, it must exist, elseValidationError("Organization not found"). - If
parentis set, aTagConfigwith thatexternal_idand the sameresourcemust exist (and, whenfacilityis set, belong to that facility), elseValueError("Parent tag config not found").
- If
validate_organizations: afacility_organizationwithout afacilityis rejected —ValueError("Facility Organization not allowed in instance level tag configs").
Server-maintained behaviour
TagConfigWriteSpec.perform_extra_deserializationresolvesparent,organization, andfacility(plusfacility_organizationscoped to the resolved facility) from theirexternal_ids onto the model instance;parentis explicitly set toNonewhen absent.TagConfigReadSpec.perform_extra_serializationsetsid = external_id, inlines the parent record viaobj.get_parent_json()(only when non-empty), and serializesfacilitywithFacilityBareMinimumSpec({ id, name }).TagConfigRetrieveSpec.perform_extra_serializationcalls the read-spec hook, thenserialize_audit_users(populatescreated_by/updated_byfrom cache) and serializesfacility_organization/organizationwith their full read specs.- Tree caches (
level_cache,parent_cache,root_tag_config,has_children) are populated by the model'sset_tag_config_cache()on insert — never accepted from clients. system_generatedis declared onTagConfigReadSpecbut is not a field on the currentTagConfigmodel (nor onEMRBaseModel). The genericserializeonly copies model fields, so it is not populated from storage unless something else provides it. Treat it as a read-only flag and verify against the live model before relying on it.
Cache invalidation
A post_save signal connects invalidate_tag_config_cache to TagConfig. Every save does three things.
- Invalidates the tag's own
TagConfigReadSpeccache and resetscached_parent_json = {}. - Invalidates all descendants (rows whose
parent_cacheoverlaps the tag id) — they embed parent data viaget_parent_json(). - Invalidates the parent's cache (its
has_childrenmay have changed).
Methods & save behaviour
set_tag_config_cache()
When a parent is set, recomputes parent_cache (parent.parent_cache + [parent.id]) and level_cache (parent.level_cache + 1), then derives root_tag_config from the parent — the parent itself if the parent is a root, otherwise the parent's root_tag_config. If the parent had no children, it flips parent.has_children = True, persisting only that field. Finishes with super().save().
get_parent_json()
Returns cached_parent_json when it is present and its cache_expiry is still in the future. Otherwise it recursively rebuilds the nested parent record (id = parent external_id, display, description, category, nested parent, level_cache), stamps a fresh expiry (cache_expiry_days = 15), persists cached_parent_json, and returns it. Returns {} for root tags.
save() side effects
On insert (no id yet), the base save() runs first, then set_tag_config_cache() populates the tree caches in a second write pass. On update, save() persists normally without recomputing caches. Both paths trigger the post_save cache-invalidation signal described above.
API integration notes
The REST API exposes only the definitions. Applied tags are stored as integer ID arrays on each tagged resource (e.g. Patient.instance_tags/facility_tags, Encounter.tags), not as join rows.
Each operation maps to one spec:
- Create —
TagConfigWriteSpec. Setsresource, the scope FKs, andparent.resourcemust match aTagResourcevalue. - Update —
TagConfigUpdateSpec. Reassigns org/facility-org only, plus the shared display/category/status/metadata fields;facility,parent, andresourceare fixed. - List —
TagConfigReadSpec. Detail —TagConfigRetrieveSpec.
metadata carries display hints (color, icon) without a schema migration, shaped by TagConfigMetadata.
Related
Pages that link here:
- Reference: References/Base models & conventions