Library internals¶
This page documents the internal mechanisms of django_xworkflows.
Binding to a workflow¶
The mechanism to bind a django model to a xworkflows.Workflow relies on the WorkflowEnabled and StateField classes.
-
class
django_xworkflows.models.StateField(django.db.models.Field)¶ This class is a simple Django
Field, specifically tuned for aWorkflow.It is internally backed by a
CharFieldcontaining thenameof the state.Reading the value always returns a
xworkflows.base.StateWrapper, writing checks that the value is a valid state or a valid state name.-
workflow¶ Mandatory; holds the
Workflowto which thisStateFieldrelates
-
choices¶ The workflow states, as a list of
(name, title)tuples, for use in forms.
-
default¶ The name of the inital state of the workflow
-
max_length¶ The length of the longest state name in the workflow.
-
blank¶ Such a field cannot be blanked (otherwise, the workflow wouldn’t have a meaning).
-
null¶ Since the field cannot be empty, is cannot be null either.
-
-
class
django_xworkflows.models.WorkflowEnabled(models.Model)¶ This class inherits from Django’s
Modelclass, performing some transformations on the subclass: eachattr = StateField(SomeWorkflow, ...)attribute will enable XWorkflows’ transition detection and wrapping.Most of this job is performed through
WorkflowEnabledMeta.-
_get_FIELD_display(self, field)¶ This method overrides the default django one to retrieve the
titlefrom aStateFieldfield.
-
Transitions¶
Transitions mostly follow XWorkflows’ mechanism.
Implementation wrappers¶
django_xworkflows provides two custom implementation wrappers specially suited for Django:
-
class
django_xworkflows.models.DjangoImplementationWrapper(xworkflows.base.ImplementationWrapper)¶ This wrapper simply adds two special attributes for interpretation in Django templates:
-
alters_data¶ Set to
Trueto prevent Django templating system to call a transition, e.g in{{ foo.confirm }}
-
do_not_call_in_templates¶ This attribute signals Django templating system (starting from Django 1.4) that the transition implementation should not be called, but its attributes should be made available.
This allows such constructs:
{% if obj.confirm.is_available %} <form method="POST" action=""> <input type="submit" value="Confirm" /> </form> {% endif %}
-
-
class
django_xworkflows.models.TransactionalImplementationWrapper(DjangoImplementationWrapper)¶ This specific wrapper runs all transition-related code, including
hooks, in a single database transaction.
The TransactionalImplementationWrapper can be enabled by setting it to
the implementation_class attribute of a xworkflows.Workflow or
of a Workflow:
class MyWorkflow(models.Workflow):
implementation_class = models.TransactionalImplementationWrapper
Workflow and logging¶
-
class
django_xworkflows.models.Workflow(xworkflows.Workflow)¶ This
xworkflows.Workflowsubclass performs a few customization:- Logging transition logs in database
- Saving updated objects after the transition
-
log_model¶ This holds the name of the model to use to log to the database. If empty, no database logging is performed.
-
log_model_class¶ This holds the class of the model to use to log to the database.
Takes precedence over
log_model. If this attribute is empty butlog_modelhas been provided, it will be filled at first access.
-
db_log(self, transition, from_state, instance, *args, **kwargs)¶ Logs the transition into the database, saving the following elements:
- Name of the transition
- Name of the initial state
GenericForeignKeyto the modified instanceForeignKeyto the user responsible for the transition- timestamp of the operation
The default
TransitionLogmodel isdjango_xworkflows.xworkflow_log.models.TransitionLog, but an alternative one can be specified inlog_modelorlog_model_class.Hint
Override this method to log to a custom TransitionLog with complex fields and storage.
-
log_transition(self, transition, from_state, instance, save=True, log=True, *args, **kwargs)¶ In addition to
xworkflows.Workflow.log_transition(), additional actions are performed:- If
saveisTrue, the instance is saved. - If
logisTrue, thedb_log()method is called to register the transition in the database.
- If
Transition database logging¶
Transition logs can be stored in the database. This is performed by the db_log() method of the Workflow class.
The default method will save informations about the transition into an adapted model. The actual model to log will be:
- The model whose class is set to the
Workflow.log_model_classattribute - The model whose name (in an
app_label.ModelClassformat) is set to theWorkflow.log_modelattribute - The
django_xworkflows.xworkflow_log.models.TransitionLogmodel ifdjango_xworkflows.xworkflow_logbelongs tosettings.INSTALLED_APPS - Nothing if none of the above match
Such models are expected to have a few fields, a good basis for writing your own is to
inherit from either BaseTransitionLog or GenericTransitionLog (which provides a default storage through a GenericForeignKey).
The BaseTransitionLog class provides all required fields for logging a transition.
-
class
django_xworkflows.models.BaseTransitionLog(models.Model)¶ This class provides minimal functions for logging a transition to the database.
-
transition¶ This attribute holds the name of the performed transition, as a string.
-
from_state¶ Name of the source state, as a string.
-
to_state¶ Name of the target state, as a string.
-
timestamp¶ Timestamp of the operation, as a
DateTimeField.
-
MODIFIED_OBJECT_FIELD¶ Name of the field where the modified instance should be passed. Logging the transition will likely fail if this is not provided.
-
EXTRA_LOG_ATTRIBUTES¶ It may be useful to log extra transition kwarg (
user, ...) to the database. This attribute describes how to log those extra keyword arguments.It takes the form of a list of 3-tuples
(db_field, kwarg, default). When logging to the database, thedb_fieldattribute of theBaseTransitionLoginstance will be filled with the keyword argument passed to the transition atkwarg, if any. Otherwise,defaultwill be used.
-
get_modified_object(self)¶ Abstract the lookup of the modified object through
MODIFIED_OBJECT_FIELD.
-
log_transition(cls, transition, from_state, to_state, modified_object, **kwargs)¶ Save a new transition log from the given transition name, origin state name, target state name, modified object and extra fields.
-
-
class
django_xworkflows.models.GenericTransitionLog(BaseTransitionLog)¶ An extended version of
BaseTransitionLoguses aGenericForeignKeyto store the modified object.-
content_type¶ A foreign key to the
ContentTypeof the modified object
-
content_id¶ The primary key of the modified object
-
modified_object¶ The
GenericForeignKeypointing to the modified object.
-
-
class
django_xworkflows.models.BaseLastTransitionLog(BaseTransitionLog)¶ This alternate
BaseTransitionLoghas been tuned to store only the last transition log for an object, typically with aOneToOneField.It handles update or creation on its own.
-
class
django_xworkflows.models.GenericLastTransitionLog(BaseLastTransitionLog)¶ This class is to
BaseLastTransitionLogwhatGenericTransitionLogis toBaseTransitionLog. It holds the modified object through aGenericForeignKey, with the adequateunique_togethersetting.
Here is an example of a custom TransitionLog model:
# Note that we inherit from BaseTransitionLog, not GenericTransitionLog.
class MyDocumentTransitionLog(django_xworkflows.models.BaseTransitionLog):
# This is where we'll store the modified object
document = models.ForeignKey(Document)
# Extra data to keep about transitions
user = models.ForeignKey(auth_models.User, blank=True, null=True)
client = models.ForeignKey(api_models.Client, blank=True, null=True)
source_ip = models.CharField(max_length=24, blank=True)
# Set the name of the field where the modified object goes
MODIFIED_OBJECT_FIELD = 'document'
# Define extra logging attributes
EXTRA_LOG_ATTRIBUTES = (
('user', 'user', None),
('client', 'api_client', None), # Transitions are called with 'api_client' kwarg
('source_ip', 'ip', ''), # Transitions are called with 'ip' kwarg
)
An example TransitionLog model is available in the django_xworkflows.xworkflow_log application.
Including it to settings.INSTALLED_APPS will enable database
logging of transitions for all WorkflowEnabled subclasses.
-
class
django_xworkflows.xworkflow_log.models.TransitionLog(GenericTransitionLog)¶ This specific
GenericTransitionLogalso stores the user responsible for the transition, if provided.The exact
Modelto use for that foreign key can be set in theXWORKFLOWS_USER_MODELdjango setting (defaults to'auth.User', which usesdjango.contrib.auth.models.User).
Internals¶
Note
These classes are private API.
-
class
django_xworkflows.models.WorkflowEnabledMeta(xworkflows.base.WorkflowEnabledMeta)¶ This metaclass is responsible for parsing a class definition, detecting all
StateFieldand collecting/defining the associatedTransactionalImplementationWrapper.-
_find_workflows(mcs, attrs)¶ Collect all
StateFieldfrom the givenattrs(the default version collectsWorkflowsubclasses instead)
-
_add_workflow(mcs, field_name, state_field, attrs)¶ Perform necessay actions to register the
Workflowstored in aStateFielddefined atfield_nameinto the given attributes dict.It differs from the base implementation which adds a
StatePropertyinstead of keeping theStateField.Parameters: - field_name (str) – The name of the attribute at which the
StateFieldwas defined - state_field (
StateField) – TheStateFieldwrapping theWorkflow - attrs (dict) – The attributes dictionary to update.
- field_name (str) – The name of the attribute at which the
-