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 a Workflow.

It is internally backed by a CharField containing the name 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.


Mandatory; holds the Workflow to which this StateField relates


The workflow states, as a list of (name, title) tuples, for use in forms.


The name of the inital state of the workflow


The length of the longest state name in the workflow.


Such a field cannot be blanked (otherwise, the workflow wouldn’t have a meaning).


Since the field cannot be empty, is cannot be null either.


Returns the south description of this field. When unfreezing, a fake Workflow will be retrieved with the same states and initial_state as present at freezing time.

This allows reading states that no longer exist in the workflow.

class django_xworkflows.models.WorkflowEnabled(models.Model)

This class inherits from Django’s Model class, performing some transformations on the subclass: each attr = 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 a StateField field.


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:


Set to True to prevent Django templating system to call a transition, e.g in {{ foo.confirm }}


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" />
{% 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

This holds the name of the model to use to log to the database. If empty, no database logging is performed.


This holds the class of the model to use to log to the database.

Takes precedence over log_model. If this attribute is empty but log_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 instance
  • ForeignKey to the user responsible for the transition
  • timestamp of the operation

The default TransitionLog model is django_xworkflows.xworkflow_log.models.TransitionLog, but an alternative one can be specified in log_model or log_model_class.


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 is True, the instance is saved.
  • If log is True, the db_log() method is called to register the transition in the database.

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:

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.


This attribute holds the name of the performed transition, as a string.


Name of the source state, as a string.


Name of the target state, as a string.


Timestamp of the operation, as a DateTimeField.


Name of the field where the modified instance should be passed. Logging the transition will likely fail if this is not provided.


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, the db_field attribute of the BaseTransitionLog instance will be filled with the keyword argument passed to the transition at kwarg, if any. Otherwise, default will be used.


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 a GenericForeignKey to store the modified object.


A foreign key to the ContentType of the modified object


The primary key of the 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 a OneToOneField.

It handles update or creation on its own.

class django_xworkflows.models.GenericLastTransitionLog(BaseLastTransitionLog)

This class is to BaseLastTransitionLog what GenericTransitionLog is to BaseTransitionLog. It holds the modified object through a GenericForeignKey, with the adequate unique_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
        ('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 the XWORKFLOWS_USER_MODEL django setting (defaults to 'auth.User', which uses django.contrib.auth.models.User).



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 associated TransactionalImplementationWrapper.

_find_workflows(mcs, attrs)

Collect all StateField from the given attrs (the default version collects Workflow subclasses instead)

_add_workflow(mcs, field_name, state_field, attrs)

Perform necessay actions to register the Workflow stored in a StateField defined at field_name into the given attributes dict.

It differs from the base implementation which adds a StateProperty instead of keeping the StateField.

  • field_name (str) – The name of the attribute at which the StateField was defined
  • state_field (StateField) – The StateField wrapping the Workflow
  • attrs (dict) – The attributes dictionary to update.