Product Knowledge
ProductKnowledge is the reusable definition of a product — its coding, base presentation unit, ingredients and nutrients, drug characteristics, and storage rules. Define it once and every concrete Product points back to it instead of carrying duplicate data. A definition is either instance-wide or scoped to a single facility.
The Django model is only the storage layer: code, names, storage_guidelines, definitional, and base_unit are opaque JSONFields whose real structure lives in the Pydantic resource specs. Every enum, nested JSON shape, validation rule, and read/write difference below comes from those specs.
Source:
- Model:
care/emr/models/product_knowledge.py - Spec:
care/emr/resources/inventory/product_knowledge/spec.py - Value sets:
care/emr/resources/inventory/product_knowledge/valueset.py
Models
| Model | Purpose |
|---|---|
ProductKnowledge | Foundational, reusable definition of a product (medication, nutritional product, or consumable) |
ProductKnowledge extends SlugBaseModel, which extends EMRBaseModel (the shared base providing external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and history/meta JSON). SlugBaseModel sets FACILITY_SCOPED = True and adds the slug helpers calculate_slug and parse_slug, so a record is addressable by an instance slug (i-<slug>) or a facility slug (f-<facility_external_id>-<slug>).
ProductKnowledge fields
Identity & scope
| Field | Type | Notes |
|---|---|---|
facility | FK → facility.Facility | PROTECT, nullable. Null means an instance-level definition; set means facility-scoped. Excluded from the spec base (__exclude__ = ["facility"]); set on write via a facility UUID, surfaced on read as is_instance_level |
slug | CharField(255) | Slug value; combined with facility to form the addressable slug. Set server-side from slug_value on write |
alternate_identifier | CharField(255) | Nullable; secondary external identifier |
status | CharField(255) | Lifecycle status. Spec-constrained to ProductKnowledgeStatusOptions (required) |
product_type | CharField(255) | Product category. Spec-constrained to ProductTypeOptions (required) |
category | FK → emr.ResourceCategory | CASCADE, nullable; classifies the product into a resource category. Write it as a category slug string, resolved via ResourceCategory.objects.get(slug=...); read returns it as a nested object |
Naming
| Field | Type | Notes |
|---|---|---|
name | CharField(255) | Primary display name (required in spec) |
names | JSONField (list) | Nullable; list of ProductName { name_type: ProductNameTypes, name: str }. name_type is bound to the ProductNameTypes enum, not a FHIR value set |
names_cache | CharField(2048) | Nullable; denormalized space-joined string of name plus every names entry, rebuilt on each save() to power search. Platform-maintained, absent from every spec — never set it from clients |
Coding & definition (JSON fields)
The model stores these as bare JSONFields; the spec enforces the shapes below.
| Field | Model type | Spec type / shape | Notes |
|---|---|---|---|
code | JSONField (dict) | Coding | None | Nullable; product code as a Coding, not value-set bound at the spec layer |
base_unit | JSONField (dict) | ValueSetBoundCoding[CARE_UCUM_UNITS] | Required. A Coding bound to the UCUM units value set (system-ucum-units) |
names | JSONField (list) | list[ProductName] | None | See ProductName |
storage_guidelines | JSONField (list) | list[StorageGuideline] | None | See StorageGuideline |
definitional | JSONField (dict) | ProductDefinitionSpec | None | Type-specific definition payload. See ProductDefinitionSpec |
Enums
ProductTypeOptions (product_type)
| Value |
|---|
medication |
nutritional_product |
consumable |
ProductKnowledgeStatusOptions (status)
| Value |
|---|
draft |
active |
retired |
unknown |
ProductNameTypes (names[].name_type)
| Value |
|---|
trade_name |
alias |
original_name |
preferred |
DrugCharacteristicCode (definitional.drug_characteristic[].code)
| Value |
|---|
imprint_code |
size |
shape |
color |
coating |
scoring |
logo |
image |
Nested spec shapes
These are plain Pydantic BaseModels in spec.py, not value-set bound unless noted.
ProductName
names[] — alternate names for the product.
| Field | Type | Required | Notes |
|---|---|---|---|
name_type | ProductNameTypes | yes | Enum, see above |
name | str | yes |
StorageGuideline
storage_guidelines[] — a storage note paired with a stability duration.
| Field | Type | Required | Notes |
|---|---|---|---|
note | str | yes | |
stability_duration | DurationSpec { value: int, unit: Coding } | yes | value is an integer count; unit is a free Coding |
ProductDefinitionSpec
definitional — type-specific definition payload.
| Field | Type | Default | Notes |
|---|---|---|---|
dosage_form | ValueSetBoundCoding[MEDICATION_FORM_CODES] | None | required (nullable) | Coding bound to medication form codes (system-medication-form-codes, SNOMED is-a 736542009) |
intended_routes | list[Coding] | [] | Free Codings, not value-set bound |
ingredients | list[ProductIngredient] | [] | See ProductIngredient |
nutrients | list[ProductNutrient] | [] | See ProductNutrient |
drug_characteristic | list[DrugCharacteristic] | [] | See DrugCharacteristic |
ProductIngredient
definitional.ingredients[].
| Field | Type | Required | Notes |
|---|---|---|---|
is_active | bool | yes | Active vs. inactive ingredient |
substance | ValueSetBoundCoding[CARE_SUBSTANCE_VALUSET] | yes | Coding bound to the substance value set (system-substance, SNOMED is-a 105590001) |
strength | ProductStrength | yes | See ProductStrength |
ProductNutrient
definitional.nutrients[].
| Field | Type | Required | Notes |
|---|---|---|---|
item | ValueSetBoundCoding[CARE_NUTRIENTS_VALUESET] | yes | Coding bound to the nutrients value set (system-nutrients, SNOMED is-a 226355009) |
amount | ProductStrength | yes | See ProductStrength |
ProductStrength
Shared by ProductIngredient.strength and ProductNutrient.amount.
| Field | Type | Required | Notes |
|---|---|---|---|
ratio | Ratio { numerator: Quantity, denominator: Quantity } | yes | |
quantity | Quantity | yes | See Quantity |
DrugCharacteristic
definitional.drug_characteristic[].
| Field | Type | Required | Notes |
|---|---|---|---|
code | DrugCharacteristicCode | yes | Enum, see above |
value | str | yes |
Shared common types
Coding
From care/emr/resources/common/coding.py (extra="forbid").
| Field | Type | Required |
|---|---|---|
system | str | None | no |
version | str | None | no |
code | str | yes |
display | str | None | no |
A ValueSetBoundCoding[<slug>] is a Coding subclass that additionally validates code against the named value set on deserialization.
Quantity
From care/emr/resources/common/quantity.py (extra="forbid").
| Field | Type | Notes |
|---|---|---|
value | Decimal | None | max 20 digits, 6 decimal places |
unit | Coding | None | human-readable unit |
code | Coding | None | machine-processable unit |
meta | dict | None |
Ratio { numerator: Quantity, denominator: Quantity } — both required.
Resource specs (API schema)
Every spec builds on EMRResource (care/emr/resources/base.py), which provides serialize (DB → Pydantic), de_serialize (Pydantic → DB), and the perform_extra_serialization / perform_extra_deserialization hooks. EMRResource.serialize copies only non-FK database fields that are declared spec fields and not in __exclude__.
| Spec class | Role | Adds / behaviour |
|---|---|---|
BaseProductKnowledgeSpec | shared base | __exclude__ = ["facility"]. Fields: id, alternate_identifier, status, product_type, code, base_unit (required, UCUM-bound), name, names, storage_guidelines, definitional |
ProductKnowledgeUpdateSpec | write · update | Adds category: str | None (resource-category slug) and slug_value: SlugType (required). On deserialize, resolves category via ResourceCategory.objects.get(slug=...) and sets obj.slug = self.slug_value |
ProductKnowledgeWriteSpec | write · create | Extends the update spec; adds facility: UUID4 | None. On deserialize, runs the update-spec logic, then resolves facility via Facility.objects.get(external_id=...) when supplied |
ProductKnowledgeReadSpec | read · detail/list | Adds is_instance_level: bool, category: dict | None, slug_config: dict, slug: str. On serialize, sets id = external_id, is_instance_level = not facility_id, serializes category via ResourceCategoryReadSpec, and sets slug_config = parse_slug(slug) |
Validation & bound value sets
status∈ProductKnowledgeStatusOptionsandproduct_type∈ProductTypeOptions, both required.base_unitis required and bound toCARE_UCUM_UNITS(system-ucum-units, systemhttp://unitsofmeasure.org).definitional.dosage_formis bound toMEDICATION_FORM_CODES;ingredients[].substancetoCARE_SUBSTANCE_VALUSET;nutrients[].itemtoCARE_NUTRIENTS_VALUESET.codeandintended_routesstay freeCodings.slug_valueusesSlugType: 5–50 chars, URL-safe (^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$), starting and ending alphanumeric.categoryon write is a slug string; an unknown slug raisesDoesNotExistfrom.get(...).
Server-maintained behaviour
facilitynever flows through the generic field loop (it sits in__exclude__); onlyProductKnowledgeWriteSpec(create) sets it, from thefacilityUUID.slugis set server-side fromslug_value;names_cacheis rebuilt in the model'ssave()(see below).- On read,
is_instance_level,slug_config, and the serializedcategoryare computed inperform_extra_serialization— none are stored columns.
Methods & save behaviour
save() side effects
save() rebuilds names_cache before persisting:
names_cacheis reset to"<name> ".- For each entry in
names, the entry'sname(dict key or attribute) is appended, space-separated. super().save()persists the record, including the slug logic inherited fromSlugBaseModel.
names_cache is platform-maintained — write to name and names instead of setting it directly.
API integration notes
- Exposed through Care's REST API; aligns with the FHIR
MedicationKnowledgeandNutritionProductconcepts. product_typedecides whatdefinitionalmeans (medication,nutritional_product, orconsumable).facilitycontrols scope: omit it for instance-wide, set it for facility-local. Address records by slug (i-<slug>orf-<facility_external_id>-<slug>); the parsed parts come back asslug_config.code,names,storage_guidelines,definitional, andbase_unitcarry their structure and value-set binding in the spec, not the model.base_unitis required.
Related
- Reference: Product
- Reference: Inventory Item
- Reference: Resource Category
- Base: Base model
- Source: model · spec · value sets