|
|
@@ -0,0 +1,167 @@ |
|
|
# Workflows (States) in Django |
|
|
|
|
|
I'm going to cover a simple, but effective, utility for managing |
|
|
state and transitions (aka workflow). |
|
|
|
|
|
We often need to store the state (status) of a model and it should |
|
|
only be in one state at a time. |
|
|
|
|
|
## Common Software Uses |
|
|
|
|
|
* Publishing (Draft->Approved->Published->Expired->Deleted) |
|
|
* Payments |
|
|
* Account Authorization (New->Active->Suspended->Deleted) |
|
|
* Membership (Trial->Paid->Cancelled) |
|
|
* Quality Assurance, Games |
|
|
* Anything with a series of steps |
|
|
|
|
|
## Definitely avoid ... |
|
|
|
|
|
Booleans for states |
|
|
|
|
|
* is_new |
|
|
* is_active |
|
|
* is_published |
|
|
* is_draft |
|
|
* is_deleted |
|
|
* is_paid |
|
|
* is_member |
|
|
* is_* |
|
|
|
|
|
Mutually exclusive states ... not _finite_ ... combinatorial |
|
|
|
|
|
## Finite State Machine |
|
|
|
|
|
* finite list of states |
|
|
* one state at a time; the **current state** |
|
|
* **transition** state by triggering event or **condition** |
|
|
|
|
|
> The behavior of state machines can be observed in many |
|
|
> devices in modern society which perform a predetermined |
|
|
> sequence of actions depending on a sequence of events |
|
|
> with which they are presented. |
|
|
- http://en.wikipedia.org/wiki/Finite-state_machine |
|
|
|
|
|
## Simple approach ... |
|
|
|
|
|
**CharField with defined choices** |
|
|
|
|
|
state = CharField( |
|
|
default=1, |
|
|
choices=[(1, "draft"), (2, "approved"), (3, "published")] |
|
|
) |
|
|
|
|
|
**Define methods to change state:** |
|
|
|
|
|
def publish(self): |
|
|
self.state = 3 |
|
|
email_sombody(self) |
|
|
self.save() |
|
|
|
|
|
def approve(self): |
|
|
self.state = 2 |
|
|
self.save() |
|
|
|
|
|
**Better, but ...** |
|
|
|
|
|
* not enforced |
|
|
* Can I go from draft to published, skipping approval? |
|
|
* What happens if I publish something that's already published? |
|
|
* repetitive |
|
|
* side-effects mix with transition code |
|
|
|
|
|
## Some Goals |
|
|
|
|
|
* Safe, verifiable transitions between states |
|
|
* Conditions for the transition |
|
|
* Clear side effects from state transitions |
|
|
* DRY |
|
|
|
|
|
## django-fsm |
|
|
|
|
|
* declarative transitions and conditions (via decorators) |
|
|
* specialized field to contain state |
|
|
|
|
|
https://github.com/kmmbvnr/django-fsm |
|
|
|
|
|
P.S. RoR has similar apps too |
|
|
|
|
|
## FSMField |
|
|
|
|
|
* Specialized CharField |
|
|
* Set `protected=True` |
|
|
* to prevent direct/accidental manipulation |
|
|
* forces use of transition methods |
|
|
* raises an AttributeError "Direct state modification is not allowed" |
|
|
|
|
|
### Example |
|
|
|
|
|
state = FSMField( |
|
|
default=State.DRAFT, |
|
|
verbose_name='Publication State', |
|
|
choices=State.CHOICES, |
|
|
protected=True, |
|
|
) |
|
|
|
|
|
(alternatives `FSMIntegerField`, `FSMKeyField`) |
|
|
|
|
|
## Transition decorator |
|
|
|
|
|
@transition(field=state, source=[State.APPROVED, State.EXPIRED], |
|
|
target=State.PUBLISHED, |
|
|
conditions=[can_display]) |
|
|
def publish(self): |
|
|
''' |
|
|
Publish the object. |
|
|
''' |
|
|
email_the_team() |
|
|
update_sitemap() |
|
|
busta_cache() |
|
|
|
|
|
What does this get us? |
|
|
|
|
|
* defined source and target states (valid transitions) |
|
|
* a method to complete the transition and define side-effects |
|
|
* a list of conditions (aside from state), that must be met for |
|
|
the transition to occur |
|
|
|
|
|
## Extras |
|
|
|
|
|
### Graphing state transitions |
|
|
|
|
|
./manage.py graph_transitions -o example-graph.png fsm_example.PublishableModel |
|
|
|
|
|
 |
|
|
|
|
|
Something a bit more complex: |
|
|
|
|
|
 |
|
|
|
|
|
### django-fsm-admin |
|
|
|
|
|
https://github.com/gadventures/django-fsm-admin |
|
|
|
|
|
* submit row |
|
|
* logging history |
|
|
* noting what's required to change state (messages) |
|
|
|
|
|
**Quick Demo** |
|
|
|
|
|
### django-fsm-log |
|
|
|
|
|
https://github.com/gizmag/django-fsm-log |
|
|
|
|
|
If you'd like your state transitions stored in something |
|
|
other than the admin history. |
|
|
|
|
|
## Alternatives? |
|
|
|
|
|
Not much out there. django-fsm has the most activity. |
|
|
|
|
|
* https://github.com/rbarrois/xworkflows |
|
|
* https://bitbucket.org/elbeanio/django-statemachine |
|
|
|
|
|
## Fin |
|
|
|
|
|
Craig Nagy @nagyman |
|
|
G Adventures - Software Engineering, eComm Mgr. |