References/Questionnaire
More languages
More actions
A Questionnaire is a versioned, FHIR-inspired form: a tree of questions answered about a patient or an encounter. You touch it whenever you define a data-collection form, render one for filling, or submit answers — which can flow back out as Observations. It captures both clinical data and arbitrary non-clinical structured data.
The Django model is only the storage layer. The API schema you code against lives in the Pydantic resource specs under care/emr/resources/questionnaire*: enums, the nested shape of the JSON fields, validation, and the split between read and write contracts.
Source (model): [https://github.com/ohcnetwork/care/blob/develop/care/emr/models/questionnaire.py care/emr/models/questionnaire.py]
Source (specs): [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/questionnaire/spec.py resources/questionnaire/spec.py] · [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/questionnaire/utils.py resources/questionnaire/utils.py] · [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/questionnaire/questionnaire_organization.py resources/questionnaire/questionnaire_organization.py] · [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/questionnaire_response/spec.py resources/questionnaire_response/spec.py] · [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/questionnaire_response_template/spec.py resources/questionnaire_response_template/spec.py] · [https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/form_submission/spec.py resources/form_submission/spec.py]
Models
Six models back the forms feature. The Questionnaire is the definition; everything else records submissions, answers, organization scope, or reusable prefill.
| Model | Purpose |
|---|---|
Questionnaire
|
A versioned form definition: questions, styling, subject type, and status |
FormSubmission
|
A submission of a questionnaire against a patient (and optional encounter) |
QuestionnaireResponse
|
A stored set of answers for a subject, optionally tied to a submission |
QuestionnaireOrganization
|
Scopes a questionnaire to an instance-level Organization
|
QuestionnaireFacilityOrganization
|
Scopes a questionnaire to a FacilityOrganization
|
QuestionnaireResponseTemplate
|
Reusable prefill template for questionnaire responses |
All extend EMRBaseModel, which supplies external_id, audit fields, and soft-delete semantics.
Questionnaire fields
Definition
| Field | Type | Req | Notes |
|---|---|---|---|
version |
CharField(255) |
yes | |
slug |
CharField(255) |
yes | |
title |
CharField(255) |
yes | |
description |
TextField |
no | |
subject_type |
CharField(255) |
yes | |
status |
CharField(255) |
yes | |
styling_metadata |
JSONField |
no | |
questions |
JSONField |
yes | |
organization_cache |
ArrayField[int] |
— | |
internal_organization_cache |
ArrayField[int] |
— |
The write spec also carries a type: str field (default "custom") that has no backing model column.
Organization scope caches
organization_cache and internal_organization_cache hold flattened organization IDs so access filtering can run without deep joins. The through-models below own them; clients never write them directly.
| Field | Maintained by |
|---|---|
organization_cache
|
QuestionnaireOrganization.sync_questionnaire_cache() — instance Organization IDs plus each org's parent_cache
|
internal_organization_cache
|
QuestionnaireFacilityOrganization.sync_questionnaire_cache() — FacilityOrganization IDs plus each org's parent_cache
|
Enum values
QuestionnaireStatus values
The lifecycle state of the definition, modeled on FHIR publication-status. Once a questionnaire is active, edit and delete are off the table — move it to retired instead.
| Value |
|---|
active
|
retired
|
draft
|
SubjectType values
What kind of resource the form is about. This drives whether an encounter is required at submit time.
| Value |
|---|
patient
|
encounter
|
QuestionType values
The type of each question (Question.type). The commented-out members open_choice, attachment, and reference are not implemented.
| Value | Notes |
|---|---|
group
|
Container; must have ≥1 sub-question |
boolean
|
Validated against true/false/1/0 on submit
|
decimal
|
|
integer
|
|
string
|
|
text
|
Length capped by settings.MAX_QUESTIONNAIRE_TEXT_RESPONSE_SIZE on submit
|
display
|
Display-only, no answer |
date
|
ISO date |
dateTime
|
Must include timezone on submit |
time
|
%H:%M:%S
|
choice
|
Requires answer_option or answer_value_set
|
url
|
Must have scheme + netloc |
quantity
|
Requires answer_option or answer_value_set; answers need a unit
|
structured
|
Skipped by response validation |
EnableOperator values
The comparison for an enable_when condition (EnableWhen.operator).
| Value |
|---|
exists
|
equals
|
not_equals
|
greater
|
less
|
greater_or_equals
|
less_or_equals
|
EnableBehavior values
How multiple enable_when conditions combine (Question.enable_behavior).
| Value | Meaning |
|---|---|
all
|
Enable only if all conditions pass (default on submit) |
any
|
Enable if any condition passes |
DisabledDisplay values
How a disabled question renders (Question.disabled_display).
| Value |
|---|
hidden
|
protected
|
AnswerConstraint values
Whether free input outside the options is allowed (Question.answer_constraint).
| Value |
|---|
required
|
optional
|
QuestionnaireResponseStatusChoices values
QuestionnaireResponse.status (model default "completed").
| Value |
|---|
completed
|
entered_in_error
|
FormSubmissionStatusChoices values
FormSubmission.status.
| Value |
|---|
draft
|
submitted
|
entered_in_error
|
Question nested shape
Questionnaire.questions is an opaque JSONField, but the API contract is a recursive list of Question (QuestionnaireBaseSpec). Fields, with their real types and validation:
| Field | Type | Req | Default | Notes |
|---|---|---|---|---|
link_id |
str |
yes | ||
id |
UUID4 |
no | ||
code |
Coding bound to value set system-observation |
no | ||
collect_time |
bool |
no | ||
collect_performer |
bool |
no | ||
text |
str |
yes | ||
description |
str |
no | ||
type |
QuestionType |
yes | ||
structured_type |
str |
no | ||
enable_when |
list[EnableWhen] |
no | ||
enable_behavior |
EnableBehavior |
no | ||
disabled_display |
DisabledDisplay |
no | ||
collect_body_site |
bool |
no | ||
collect_method |
bool |
no | ||
required |
bool |
no | ||
repeats |
bool |
no | ||
read_only |
bool |
no | ||
max_length |
int |
no | ||
answer_constraint |
AnswerConstraint |
no | ||
answer_option |
list[AnswerOption] |
no | ||
answer_value_set |
str |
no | ||
is_observation |
bool |
no | ||
unit |
Coding bound to value set system-ucum-units |
no | ||
questions |
list[Question] |
no | ||
formula |
str |
no | ||
styling_metadata |
dict |
no | ||
templates |
list[TemplateConfig] |
no | ||
is_component |
bool |
no |
A model_validator (mode after) enforces three rules:
choice/quantitytypes must haveanswer_optionoranswer_value_set.grouptypes must have at least one sub-question.answer_value_set, when set, must reference an existingValueSet.
Sub-specs of Question
| Spec | Shape |
|---|---|
EnableWhen
|
{ question: str (link_id), operator: EnableOperator, answer: Any }
|
AnswerOption
|
{ value: Any (non-blank, stripped), initial_selected: bool = false }
|
Performer
|
None, text: str | None } |
TemplateConfig
|
None, meta: dict | None } |
Resource specs (API schema)
Every spec builds on EMRResource, which provides serialize / de_serialize. Read specs run perform_extra_serialization; write specs run perform_extra_deserialization.
Questionnaire
| Spec | Role | Key fields / behaviour |
|---|---|---|
QuestionnaireBaseSpec
|
shared | __model__ = Questionnaire
|
QuestionnaireWriteSpec
|
write (base) | version (frozen "1.0"), slug (SlugType), title, description, type ("custom"), status, subject_type, styling_metadata, questions. Validates slug uniqueness and that it doesn't shadow internal types, a non-empty title, and unique link_ids and ids across the whole tree
|
QuestionnaireSpec
|
write · create
Related
|