Views
For design reasons explained elsewhere, function-based views were preferred over class-based views. This means that each field in a given model is updated in isolation of others through individual ajax post requests direct from the template.
Decorators¶
Decorators are used to protect the views.
@login_required
: Nearly all routes are decorated by the django decorator and redirect to the login page@user_may_view_this_organisation()
: The is a custom decorator which only allows access to the logged in user whose employing organisation (as stored in the request.user object) matches that of the lead Epilepsy centre for the child. Superusers or RCPCH members may see all children nationally. Failure redirects to 403@user_may_view_this_child()
: This is a custom decorator which allows only the logged-in user access to view or edit data relating to a given child in their same organisation. Failure redirects to 403user_can_access_user
: This is a custom decorator which allows only the logged-in user access to view or edit data relating to a given user in their same organisation. Failure redirects to 403@permission_required("epilepsy12.change_episode", raise_exception=True)
View structure¶
View functions mirror the structure of the models. Each model has a corresponding view in the views
folder in the root of the epilepsy12
folder. The first function in each file is called to load the form template. If no instance of that model exists, an instance is created. Any dependencies for the template (such as lists for select dropdowns etc) are retrieved here and added to the context to be passed on to the template.
View functions¶
Any function that updates, creates or deletes a model, early on in the function, calls validate_and_update_model
in epilepsy12/common_view_functions/
. It accepts the following parameters:
request
: request object passed in from calling viewmodel_id
: id of the model to updatemodel
: the Model itselffield_name
: the name of the field to be updated as stringpage_element
: the type of page element selector, one ofdate_field
,hospitals_select
,multiple_choice_multiple_toggle_button
,select
,single_choice_multiple_toggle_button
,toggle_button
,snomed_select
comparison_date_field_name=None
: if the selector is adate_field
, additional parameters are required for validationis_earliest_date=None
: if the selector is adate_field
, additional parameters are required for validationearliest_allowable_date=None
: if the selector is adate_field
, additional parameters are required for validation
For the final 3 optional parameters, see the section on date validation in validators.py
Any validations that identify errors are raised here with messages that are caught in the view. Otherwise the model is updated with the new value.
The name of the view function matches the name of the field of the model affected. It is passed also in the url.
Example¶
@login_required
@user_may_view_this_child()
@permission_required("epilepsy12.change_episode", raise_exception=True)
def seizure_onset_date(request, episode_id):
"""
HTMX post request from episode.html partial on date change
"""
try:
episode = Episode.objects.get(pk=episode_id)
error_message = None
validate_and_update_model(
request=request,
model=Episode,
model_id=episode_id,
field_name="seizure_onset_date",
page_element="date_field",
earliest_allowable_date=None, # episodes may precede the first assessment date or cohort date
)
except ValueError as error:
error_message = error
keywords = Keyword.objects.all()
episode = Episode.objects.get(pk=episode_id)
context = {
"episode": episode,
"seizure_onset_date_confidence_selection": DATE_ACCURACY,
"episode_definition_selection": EPISODE_DEFINITION,
"keyword_selection": keywords,
"epilepsy_or_nonepilepsy_status_choices": sorted(
EPILEPSY_DIAGNOSIS_STATUS, key=itemgetter(1)
),
"epileptic_seizure_onset_types": sorted(
EPILEPSY_SEIZURE_TYPE, key=itemgetter(1)
),
"GENERALISED_SEIZURE_TYPE": sorted(GENERALISED_SEIZURE_TYPE, key=itemgetter(1)),
"LATERALITY": LATERALITY,
"FOCAL_EPILEPSY_MOTOR_MANIFESTATIONS": FOCAL_EPILEPSY_MOTOR_MANIFESTATIONS,
"FOCAL_EPILEPSY_NONMOTOR_MANIFESTATIONS": FOCAL_EPILEPSY_NONMOTOR_MANIFESTATIONS,
"FOCAL_EPILEPSY_EEG_MANIFESTATIONS": FOCAL_EPILEPSY_EEG_MANIFESTATIONS,
"nonepilepsy_onset_types": NON_EPILEPSY_SEIZURE_ONSET,
"nonepilepsy_types": sorted(NON_EPILEPSY_SEIZURE_TYPE, key=itemgetter(1)),
"syncopes": sorted(NON_EPILEPTIC_SYNCOPES, key=itemgetter(1)),
"behavioural": sorted(
NON_EPILEPSY_BEHAVIOURAL_ARREST_SYMPTOMS, key=itemgetter(1)
),
"sleep": sorted(NON_EPILEPSY_SLEEP_RELATED_SYMPTOMS, key=itemgetter(1)),
"paroxysms": sorted(NON_EPILEPSY_PAROXYSMS, key=itemgetter(1)),
"migraines": sorted(MIGRAINES, key=itemgetter(1)),
"nonepilepsy_miscellaneous": sorted(EPIS_MISC, key=itemgetter(1)),
"epilepsy_cause_selection": EPILEPSY_CAUSES,
}
response = recalculate_form_generate_response(
model_instance=episode.multiaxial_diagnosis,
request=request,
template="epilepsy12/partials/multiaxial_diagnosis/episode.html",
context=context,
error_message=error_message,
)
return response
This function is called from the following path in urls.py
, as an HTMX POST request from the template, called in the change event of the custom page element date_field
.
path(
"episode/<int:episode_id>/seizure_onset_date",
views.seizure_onset_date,
name="seizure_onset_date",
),
seizure_onset_date
is a field in the Episode
model. The request contains the episode_id
and the new date in the header. It can be accessed by request.POST.get(request.htmx.trigger_name)
.
After the episode to be updated with the new date has been retrieved using the theepisode_id
, these parameters are passed intovalidate_and_update_model
where the date is retrieved, validated and the model updated. If there are any validation errors, these are raised here, and caught in the try...except
block and stored in the error_message
variable.
The context
is updated in the recalculate_form_generate_response
discussed elsewhere before being passed back to the template. This latter function also calculates the number of scored fields in the form and updates the totals in the steps.html
partial by adding an HTMX custom trigger to the header.