Skip to content

Instantly share code, notes, and snippets.

@peterfarrell
Forked from Nagyman/workflows-in-django.md
Created June 20, 2016 12:55
Show Gist options
  • Save peterfarrell/2d17175bc3f907da7f4552d5aeb622ce to your computer and use it in GitHub Desktop.
Save peterfarrell/2d17175bc3f907da7f4552d5aeb622ce to your computer and use it in GitHub Desktop.

Revisions

  1. Craig Nagy revised this gist Mar 14, 2014. 1 changed file with 11 additions and 5 deletions.
    16 changes: 11 additions & 5 deletions workflows-in-django.md
    Original file line number Diff line number Diff line change
    @@ -1,10 +1,9 @@
    # 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.
    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

    @@ -28,7 +27,14 @@ Booleans for states
    * is_member
    * is_*

    Mutually exclusive states ... not _finite_ ... combinatorial
    Mutually exclusive states ... sort of finite, but the number
    of states increases with each bool:

    * 2 bools = 2^2 = 4 states
    * 3 bools = 2^3 = 8 states
    * etc (2^N)

    Brittle and too many states to check.

    ## Finite State Machine

  2. Craig Nagy revised this gist Mar 12, 2014. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion workflows-in-django.md
    Original file line number Diff line number Diff line change
    @@ -145,7 +145,7 @@ https://github.com/gadventures/django-fsm-admin
    * logging history
    * noting what's required to change state (messages)

    **Quick Demo**
    ![](http://i.imgur.com/Dw76BZ9.png)

    ### django-fsm-log

  3. Craig Nagy created this gist Mar 12, 2014.
    167 changes: 167 additions & 0 deletions workflows-in-django.md
    Original file line number Diff line number Diff line change
    @@ -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

    ![](http://i.imgur.com/RxHsaDl.png)

    Something a bit more complex:

    ![](http://i.imgur.com/DTnM9CW.png)

    ### 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.