Skip to content

Commit 2975fea

Browse files
committed
docs: merge mixins.md into integrations.md and move API ref to api.md
Remove the standalone mixins.md page (not in toctree). Move the MachineMixin autoclass reference to api.md alongside other API docs, and consolidate the usage examples into integrations.md with a cleaner narrative flow.
1 parent 1c998a1 commit 2975fea

3 files changed

Lines changed: 113 additions & 115 deletions

File tree

docs/api.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,18 @@ class MyMachine(StateChart):
126126
...
127127
```
128128

129+
## MachineMixin
130+
131+
```{seealso}
132+
{ref}`Integrations <machinemixin>` for usage examples.
133+
```
134+
135+
```{eval-rst}
136+
.. autoclass:: statemachine.mixins.MachineMixin
137+
:members:
138+
:undoc-members:
139+
```
140+
129141
## create_machine_class_from_definition
130142

131143
```{versionadded} 3.0.0

docs/integrations.md

Lines changed: 101 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,125 @@
11

22
# Integrations
33

4-
(django integration)=
54
(machinemixin)=
5+
## MachineMixin
6+
7+
{ref}`Domain models` can inherit from `MachineMixin` to automatically instantiate
8+
and bind a {ref}`StateChart` to any Python class. This is the foundation for
9+
integrating state machines with ORMs and other domain objects.
10+
11+
```{seealso}
12+
See the [MachineMixin API reference](api.md#machinemixin) for the full list of attributes.
13+
```
14+
15+
### Example
16+
17+
Given this state machine:
18+
19+
```py
20+
>>> from statemachine import StateChart, State
21+
22+
>>> from statemachine.mixins import MachineMixin
23+
24+
>>> class CampaignMachine(StateChart):
25+
... "A workflow machine"
26+
... draft = State('Draft', initial=True, value=1)
27+
... producing = State('Being produced', value=2)
28+
... closed = State('Closed', value=3, final=True)
29+
... cancelled = State('Cancelled', value=4, final=True)
30+
...
31+
... add_job = draft.to.itself() | producing.to.itself()
32+
... produce = draft.to(producing)
33+
... deliver = producing.to(closed)
34+
... cancel = cancelled.from_(draft, producing)
35+
36+
```
37+
38+
You can attach it to a model by inheriting from `MachineMixin` and setting
39+
`state_machine_name` to the fully qualified class name:
40+
41+
``` py
42+
>>> class Workflow(MachineMixin):
43+
... state_machine_name = '__main__.CampaignMachine'
44+
... state_machine_attr = 'sm'
45+
... state_field_name = 'workflow_step'
46+
... bind_events_as_methods = True
47+
...
48+
... workflow_step = 1
49+
50+
```
51+
52+
When a `Workflow` instance is created, it automatically receives a `CampaignMachine`
53+
instance at the `state_machine_attr` attribute. The state value is read from and
54+
written to the `state_field_name` field:
55+
56+
``` py
57+
>>> model = Workflow()
58+
59+
>>> isinstance(model.sm, CampaignMachine)
60+
True
61+
62+
>>> model.workflow_step
63+
1
64+
65+
>>> model.sm.draft in model.sm.configuration
66+
True
67+
68+
```
69+
70+
With `bind_events_as_methods = True`, events become methods on the model itself:
71+
72+
``` py
73+
>>> model.produce()
74+
>>> model.workflow_step
75+
2
76+
77+
>>> model.sm.cancel() # you can still call the SM directly
78+
79+
>>> model.workflow_step
80+
4
81+
82+
>>> model.sm.cancelled in model.sm.configuration
83+
True
84+
85+
```
86+
87+
```{note}
88+
In this example `state_machine_name` uses a `__main__` prefix because the class
89+
is defined inline for doctest purposes. In your code, use the fully qualified
90+
path (e.g., `'myapp.statemachines.CampaignMachine'`).
91+
```
92+
93+
(django integration)=
694
## Django integration
795

896
When used in a Django App, this library implements an auto-discovery hook similar to how Django's
997
built-in **admin** [autodiscover](https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.autodiscover).
1098

11-
> This library attempts to import an **statemachine** or **statemachines** module in each installed
99+
> This library attempts to import a **statemachine** or **statemachines** module in each installed
12100
> application. Such modules are expected to register `StateChart` classes to be used with
13101
> the {ref}`MachineMixin`.
14102
15103

16104
```{hint}
17-
When using `python-statemachine` to control the state of a Django model, we advise keeping the
18-
{ref}`StateChart` definitions on their own modules.
105+
We advise keeping {ref}`StateChart` definitions in their own modules to avoid circular
106+
references. If you place state machines in modules named `statemachine` or `statemachines`
107+
inside installed Django Apps, they will be automatically imported and registered.
19108
20-
So as circular references may occur, and as a way to help you organize your
21-
code, if you put state machines on modules named as mentioned above inside installed
22-
Django Apps, these {ref}`StateChart` classes will be automatically
23-
imported and registered.
24-
25-
This is only an advice, nothing stops you do declare your state machine alongside your models.
109+
That said, nothing stops you from declaring your state machine alongside your models.
26110
```
27111

28112

29-
### Example
30-
31-
Given this StateMachine:
113+
### Django example
32114

33115
```py
34116
# campaign/statemachines.py
35117

36118
from statemachine import StateChart
37119
from statemachine import State
38-
from statemachine.mixins import MachineMixin
39120

40121

41-
class CampaignMachineWithKeys(StateChart):
122+
class CampaignMachine(StateChart):
42123
"A workflow machine"
43124
draft = State('Draft', initial=True, value=1)
44125
producing = State('Being produced', value=2)
@@ -51,27 +132,25 @@ class CampaignMachineWithKeys(StateChart):
51132
cancel = cancelled.from_(draft, producing)
52133
```
53134

54-
Integrate with your model:
135+
Integrate with your Django model using `MachineMixin`:
55136

56137
```py
57138
# campaign/models.py
58139

59140
from django.db import models
60141

142+
from statemachine.mixins import MachineMixin
143+
144+
61145
class Campaign(models.Model, MachineMixin):
62-
state_machine_name = 'campaign.statemachines.CampaignMachineWithKeys'
146+
state_machine_name = 'campaign.statemachines.CampaignMachine'
63147
state_machine_attr = 'sm'
64148
state_field_name = 'step'
65149

66150
name = models.CharField(max_length=30)
67151
step = models.IntegerField()
68152
```
69153

70-
71-
```{seealso}
72-
Learn more about using the [](mixins.md#machinemixin).
73-
```
74-
75154
### Data migrations
76155

77156
Django's `apps.get_model()` returns **historical model** classes that are dynamically created

docs/mixins.md

Lines changed: 0 additions & 93 deletions
This file was deleted.

0 commit comments

Comments
 (0)