Class-based views¶
Class-based views are highly practical for the add/edit view of physical
processes because they keep code duplication at a minimum. In some cases, you
get away with only a few lines of code. Mixin classes reduce the redundancy
further. Although it is still possible to have ordinary view functions for
physical processes, we do not recommend this. If you follow the convention of
calling your view class “EditView
” and place it in a module called
class_name.py
, the PatternGenerator
will detect it and create the URL dispatch for it.
The API¶
The API of JuliaBase’s class-based view classes is best described by discussing
the attributes and methods of the common base class
ProcessWithoutSamplesView
. Not
only if you derive your views, but also if you need to define your own
abstract view class, you should derive it from one of the concrete classes
presented in the next section, though, because you probably want to re-use part
of their functionality.
This class is found in the module samples.utils.views.class_views
.
- class ProcessWithoutSamplesView(**kwargs)¶
Abstract base class for the classed-based views. It deals only with the process per se, and in partuclar, with no samples associated with this process. This is done in the concrete derived classes
ProcessView
(one sample) andProcessMultipleSamplesView
(multiple samples). So, you should never instantiate this one.The methods that you most likely want to redefine in you own concrete class are, with decreasing probability:
Note that for
is_referentially_valid()
,save_to_database()
,build_forms()
, andget_context_data()
, it is necessary to call the inherited method.Since you connect forms with the view class, the view class expects certain constructor signatures of the forms. As for the process model form, it must accept the logged-in user as the first argument. This is the case for
ProcessForm
andDepositionForm
, so this should not be a problem. The derived class (see below) may impose constrains on their external forms either.- Variables
form_class – The model form class of the process of this view.
model – The model class of the view. If not given, it is derived from the process form class.
class_name – The name of the model class, e.g.
"clustertooldeposition"
.process – The process instance this view is about. If we are about to add a new process, this is
None
until the respective form is saved.forms – A dictionary mapping template context names to forms, or lists of forms. Mandatory keys in this dictionary are
"process"
and"edit_description"
. (Derived classes add"sample"
,"samples"
,"remove_from_my_samples"
,"layers"
, etc.)data – The POST data if we have a POST request, or
None
otherwise.id – The ID of the process to edit, or
None
if we are about to add a new one. This is the recommended way to distinguish between editing and adding.preset_sample – The sample with which the process should be connected by default. May be
None
.request – The current request object. This is inherited from Django’s view classes.
template_name – The file name of the rendering template, with the same path syntax as in the
render()
function.identifying_field – The name of the field in the process which is the poor man’s primary key for this process, e.g. the deposition number. It is taken from the model class.
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- build_forms()¶
Fills the
forms
dictionary with the forms, or lists of them. In this base class, we only add"process"
itself and"edit_description"
. Note that the dictionary key is later used in the template as context variable.This method has no parameters and no return values,
self
is modified in-situ. It is good habit to check for a key before setting it, allowing derived methods to set it themselves without doing double work.
- get_context_data(**kwargs)¶
Generates the template context. In particular, we inject the forms and the title here into the context. This method is part of the official Django API.
- Return
the context dict
- Return type
dict
- get_next_id()¶
Gets the next identifying value for the process class. In its default implementation, it just takes the maximal existing value and adds 1. This needs to be overridden if the identifying field is non-numeric.
- Return
The next untaken of the identifying field, e.g. the next free deposition number.
- Return type
object
- get_title()¶
Creates the title of the response. This is used in the
<title>
tag and the<h1>
tag at the top of the page.- Return
the title of the response
- Return type
str
- is_all_valid()¶
Checks whether all forms are valid. Moreover, this method guarantees that the
is_valid()
method of every form is called in order to collect all error messages.You may mark any unbound form as valid for this method by setting its attribute
dont_check_validity
toTrue
. If it is not present, it is assumed to beFalse
. This is helpful for marking unbound forms that should not let the request fail during a POST request. An example is a form in which the user enters the number of to-be-added sub-processes. It is reset (emptied) after each POST request by setting it to a pristine unbound form. However, this must not prevent the view from succeeding.- Return
whether all forms are valid
- Return type
bool
- is_referentially_valid()¶
Checks whether the data of all forms is consistent with each other and with the database. This is the partner of
is_all_valid()
but checks the inter-relations of data.This method is frequently overriden in concrete view classes.
Note that a
True
here does not imply aTrue
fromis_all_valid()
. Both methods are independent of each other. In particular, you must check the validity of froms that you use here.- Return
whether the data submitted to the view is valid
- Return type
bool
- save_to_database()¶
Saves the data to the database.
- Return
the saved process instance
- Return type
samples.models.PhysicalProcess
- startup()¶
Fetch the process to-be-edited from the database and check permissions. This method has no parameters and no return values,
self
is modified in-situ.
Main classes¶
The following names are found in the module samples.utils.views
.
- class ProcessView(**kwargs)¶
View class for physical processes with one sample each. The HTML form for the sample is called
sample
in the template. Typical usage can be very short:from samples.utils.views import ProcessForm, ProcessView class LayerThicknessForm(ProcessForm): class Meta: model = LayerThicknessMeasurement fields = "__all__" class EditView(ProcessView): form_class = LayerThicknessForm
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- class ProcessMultipleSamplesView(**kwargs)¶
View class for physical processes with one or more samples each. The HTML form for the sample list is called
samples
in the template. The usage is analogous toProcessView
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- class DepositionView(**kwargs)¶
View class for views for depositions with layers. The layers of the process must always be of the same type. If they are not, you must use
DepositionMultipleTypeView
instead. Additionally toform_class
, you must set thestep_form_class
class variable to the form class to be used for the layers.The layer form should be a subclass of
SubprocessForm
.Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- class DepositionMultipleTypeView(**kwargs)¶
View class for depositions the layers of which are of different types (i.e., different models). You can see it in action in the module
institute.views.samples.cluster_tool_deposition
. Additionally to the class variableform_class
, you must set:- Variables
step_form_classes – This is a tuple of the form classes for the layers
short_labels – (optional) This is a dict mapping a layer form class to a concise name of that layer type. It is used in the selection widget of the add-step form.
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- class SubprocessForm(view, *args, **kwargs)¶
Model form class for subprocesses. Its only purpose is to eat up the
view
parameter to the constructor so that you need not redefine the constructor every time.
- class SubprocessMultipleTypesForm(view, data=None, **kwargs)¶
Abstract model form for all step types in a process. It is to be used in conjunction with
MultipleStepTypesMixin
. See the views of th cluster-tool deposition in the INM “institute” app for an example for how to use this class.
Mixins¶
- class RemoveFromMySamplesMixin(**kwargs)¶
Mixin for views that like to offer a “Remove from my samples” button. In the template, they may add the following code:
{{ remove_from_my_samples.as_p }}
This mixin must come before the main view class in the list of parents.
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- class SamplePositionsMixin(**kwargs)¶
Mixin for views that need to store the positions the samples used to have during the processing. The respective process class must inherit from
ProcessWithSamplePositions
. In the edit template, you must add the following code:{% include "samples/edit_sample_positions.html" %}
This mixin must come before the main view class in the list of parents.
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- class SubprocessesMixin(**kwargs)¶
Mixing for views that represent processes with subprocesses. Have a look at
institute.views.samples.solarsimulator_measurement
for an example. In a way, it is a light-weight variant of theMultipleStepsMixin
below. In contrast to that, this mixin doesn’t order its subprocesses (you still may enforce ordering in the show template).For this to work, you must define the following additional class variables:
subform_class
: the model form class for the subprocessesprocess_field
: the name of the field of the parent process in the subprocess modelsubprocess_field
: therelated_name
parameter in the field of the parent process in the subprocess model
You should derive the model form class of the subprocess from
SubprocessForm
. This is not mandatory but convenient, see there.In the template, the forms of the subprocesses are available in a list called
subprocesses
. Furthermore, you should include{{ number.as_p }}
in the template so that the user can set the number of subprocesses.
This mixin must come before the main view class in the list of parents.
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- class MultipleStepsMixin(**kwargs)¶
Mixin class for views for processes with steps. The steps of the process must always be of the same type. If they are not, you must use
MultipleTypeStepsMixin
instead. The step model must include a field called “number
”, which should be ordered. This mixin must come before the main view class in the list of parents.You can see it in action in the module
institute.views.samples.five_chamber_deposition
. In the associated edit template, you can also see the usage of the three additional template variablessteps
,change_steps
(well, at least their combinationsteps_and_change_steps
), andadd_steps
.Additionally to
form_class
, you must set the following class variables:- Variables
step_form_class – the form class to be used for the steps.
process_field – to the name of the field of the parent process in the step model.
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
- class MultipleStepTypesMixin(**kwargs)¶
Mixin class for processes the steps of which are of different types (i.e., different models). The step model must include a field called “
number
”, which should be ordered. This mixin must come before the main view class in the list of parents.You can see it in action in the module
institute.views.samples.cluster_tool_deposition
. In the associated edit template, you can also see the usage of the three additional template variablessteps
,change_steps
(well, at least their combinationsteps_and_change_steps
), andadd_steps
. Moreover, note the use of thestep_type
andtype
fields of each layer (= step).Additionally to the class variable
form_class
, you must set:- Variables
step_form_classes – This is a tuple of the form classes for the steps
process_field – to the name of the field of the parent process in the step model.
short_labels – (optional) This is a dict mapping a step form class to a concise name of that step type. It is used in the selection widget of the add-step form.
Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things.
Sub-processes¶
Quite often, there is the need to divide a process further into sub-processes. JuliaBase realises this by special mixin classes. In this section, I discuss the bigger picture of it. Look at the reference above for the details and further information.
There are three mixins that deal with sub-processes:
SubprocessesMixin
¶
This is a lightweight solution if you just want to have the forms for sub-processes auto-generated. It lets you create an edit/add view which allows the user to enter the number of sub-processes, and to enter the sub-processes’ data. This mixin does not enforce any ordering of the sub-processes – you may, howvever, enforce an ordering in the show view yourself, possibly by a model setting.
Because of the lack of user convenience, this mixin is useful particularly for edit/add views which are primarily used by programs (e.g. crawlers) rather than by human beings. The solar simulator of the INM institute app demonstrates how to use this mixin.
MultipleStepsMixin
¶
This mixin realises the JuliaBase concept of a step. Steps are ordered
sub-processes. On the model layer, they are models not derived from
Process
that contain an interger field number
. This field is used to
define the ordering, and helps JuliaBase to provide some convenience
functionality: Re-ordering steps, duplicating them, deleting them. The parent
model, for example the deposition process class, must define a method
steps()
that returns a query set of all steps, as in:
def get_steps(self):
return self.layers
The MultipleStepsMixin
is the main ingredient for the class
DepositionView
. You can see the latter in
action in the 5-chamber deposition views of the INM institute app.
“My steps”¶
Moreover with steps, your users can use something called “my steps”. It is a list of favourite steps that occur frequently. Every step in this list has a nickname, chosen by the user. When composing a new process, the user can select from this list instead of entering the step data manually. JuliaBase stores the “my steps” list for each user, however, you must add a view that lets the user set this list for each process class(es) that should be supported. In JuliaBase’s example app “institute”, a “my layers” view is included which realises this functionality for deposition layers.
MultipleStepTypesMixin
¶
This mixin is the same as above, but each step may be of a different model
class. Using this mixin is slightly more complicated but also more powerful
for obvious reasons. The steps must be of a common base model class, of which
the concrete model classes are derived. Consequently, the base model class
must inherit from jb_common.models.PolymorphicModel
. The
steps()
method may return instances of the base model class, because
the class-based view already takes care of finding the actual instance.
In the view model, you should derive the forms classes for your step types from
SubprocessMultipleTypesForm
. This takes care
of an extra field step_type
that helps the view class to identify the step
type from he HTTP POST data.
This forms the basis of
DepositionMultipleTypeView
. You can see the
latter in action in the cluster-tool deposition views of the INM institute app.