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
896When used in a Django App, this library implements an auto-discovery hook similar to how Django's
997built-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
36118from statemachine import StateChart
37119from 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
59140from django.db import models
60141
142+ from statemachine.mixins import MachineMixin
143+
144+
61145class 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
77156Django's ` apps.get_model() ` returns ** historical model** classes that are dynamically created
0 commit comments