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
CharField
containing thename
of 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
Workflow
to which thisStateField
relates
-
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
Model
class, 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
title
from aStateField
field.
-
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
True
to 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.Workflow
subclass 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_model
has 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
GenericForeignKey
to the modified instanceForeignKey
to the user responsible for the transition- timestamp of the operation
The default
TransitionLog
model isdjango_xworkflows.xworkflow_log.models.TransitionLog
, but an alternative one can be specified inlog_model
orlog_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
save
isTrue
, the instance is saved. - If
log
isTrue
, 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_class
attribute - The model whose name (in an
app_label.ModelClass
format) is set to theWorkflow.log_model
attribute - The
django_xworkflows.xworkflow_log.models.TransitionLog
model ifdjango_xworkflows.xworkflow_log
belongs 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_field
attribute of theBaseTransitionLog
instance will be filled with the keyword argument passed to the transition atkwarg
, if any. Otherwise,default
will 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
BaseTransitionLog
uses aGenericForeignKey
to store the modified object.-
content_type
¶ A foreign key to the
ContentType
of the modified object
-
content_id
¶ The primary key of the modified object
-
modified_object
¶ The
GenericForeignKey
pointing to the modified object.
-
-
class
django_xworkflows.models.
BaseLastTransitionLog
(BaseTransitionLog)¶ This alternate
BaseTransitionLog
has 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
BaseLastTransitionLog
whatGenericTransitionLog
is toBaseTransitionLog
. It holds the modified object through aGenericForeignKey
, with the adequateunique_together
setting.
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
GenericTransitionLog
also stores the user responsible for the transition, if provided.The exact
Model
to use for that foreign key can be set in theXWORKFLOWS_USER_MODEL
django 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
StateField
and collecting/defining the associatedTransactionalImplementationWrapper
.-
_find_workflows
(mcs, attrs)¶ Collect all
StateField
from the givenattrs
(the default version collectsWorkflow
subclasses instead)
-
_add_workflow
(mcs, field_name, state_field, attrs)¶ Perform necessay actions to register the
Workflow
stored in aStateField
defined atfield_name
into the given attributes dict.It differs from the base implementation which adds a
StateProperty
instead of keeping theStateField
.Parameters: - field_name (str) – The name of the attribute at which the
StateField
was defined - state_field (
StateField
) – TheStateField
wrapping theWorkflow
- attrs (dict) – The attributes dictionary to update.
- field_name (str) – The name of the attribute at which the
-