v1k45: @aleneum Thanks for the review, please take another look when you get time. :)
diff --git a/README.md b/README.md
index 850ffba..0b35862 100644
--- a/README.md
+++ b/README.md
@@ -319,6 +319,8 @@ machine = Machine(lump, states=['A', 'B', 'C'])
 
 Now, any time `lump` transitions to state `A`, the `on_enter_A()` method defined in the `Matter` class will fire.
 
+#### <a name="checking-state"></a>Checking state
+
 You can always check the current state of the model by either:
 
 - inspecting the `.state` attribute, or
@@ -337,6 +339,15 @@ machine.get_state(lump.state).name
 >>> 'solid'
 ```
 
+If you'd like you track it using a different attribute, you could do that using the `model_attribute` argument while initializing the `Machine`.
+
+```python
+lump = Matter()
+machine = Machine(lump, states=['solid', 'liquid', 'gas'],  model='matter_state', initial='solid')
+lump.matter_state
+>>> 'solid'
+```
+
 #### <a name="enum-state"></a>Enumerations
 
 So far we have seen how we can give state names and use these names to work with our state machine. If you favour stricter typing and more IDE code completion (or you just can't type 'sesquipedalophobia' any longer because the word scares you) using [Enumerations](https://docs.python.org/3/library/enum.html) might be what you are looking for:
@@ -913,6 +924,20 @@ machine.add_model(Matter())
 machine.add_model(Matter(), initial='liquid')
 ```
 
+Models with multiple states could attach multiple machines using different `model_attribute` values:
+
+```python
+lump = Matter()
+
+lump.state
+>>> 'solid'
+lump.shipping_state
+>>> 'delivered'
+
+matter_machine = Machine(lump, model_attribute='state', **kwargs)
+shipment_machine = Machine(lump, model_attribute='shipping_state', **kwargs)
+```
+
 ### Logging
 
 Transitions includes very rudimentary logging capabilities. A number of events – namely, state changes, transition triggers, and conditional checks – are logged as INFO-level events using the standard Python `logging` module. This means you can easily configure logging to standard output in a script:
diff --git a/transitions/core.py b/transitions/core.py
index fb29a81..6fdb506 100644
--- a/transitions/core.py
+++ b/transitions/core.py
@@ -290,7 +290,7 @@ class Transition(object):
     def _change_state(self, event_data):
         event_data.machine.get_state(self.source).exit(event_data)
         event_data.machine.set_state(self.dest, event_data.model)
-        event_data.update(event_data.model.state)
+        event_data.update(getattr(event_data.model, event_data.machine.model_attribute))
         event_data.machine.get_state(self.dest).enter(event_data)
 
     def add_callback(self, trigger, func):
@@ -408,7 +408,7 @@ class Event(object):
         """ Internal trigger function called by the ``Machine`` instance. This should not
         be called directly but via the public method ``Machine.trigger``.
         """
-        state = self.machine.get_state(model.state)
+        state = self.machine.get_model_state(model)
         if state.name not in self.transitions:
             msg = "%sCan't trigger event %s from state %s!" % (self.machine.name, self.name,
                                                                state.name)
@@ -496,7 +496,7 @@ class Machine(object):
                  send_event=False, auto_transitions=True,
                  ordered_transitions=False, ignore_invalid_triggers=None,
                  before_state_change=None, after_state_change=None, name=None,
-                 queued=False, prepare_event=None, finalize_event=None, **kwargs):
+                 queued=False, prepare_event=None, finalize_event=None, model_attribute='state', **kwargs):
         """
         Args:
             model (object or list): The object(s) whose states we want to manage. If 'self',
@@ -570,6 +570,7 @@ class Machine(object):
         self.after_state_change = after_state_change
         self.finalize_event = finalize_event
         self.name = name + ": " if name is not None else ""
+        self.model_attribute = model_attribute
 
         self.models = []
 
@@ -728,7 +729,10 @@ class Machine(object):
         Returns:
             bool: Whether the model's current state is state.
         """
-        return model.state == state
+        return getattr(model, self.model_attribute) == state
+
+    def get_model_state(self, model):
+        return self.get_state(getattr(model, self.model_attribute))
 
     def set_state(self, state, model=None):
         """
@@ -740,7 +744,7 @@ class Machine(object):
         models = self.models if model is None else listify(model)
 
         for mod in models:
-            mod.state = state.value
+            setattr(mod, self.model_attribute, state.value)
 
     def add_state(self, *args, **kwargs):
         """ Alias for add_states. """
@@ -853,6 +857,8 @@ class Machine(object):
             **kwargs: Additional arguments which can be passed to the created transition.
                 This is useful if you plan to extend Machine.Transition and require more parameters.
         """
+        if trigger == self.model_attribute:
+            raise ValueError("Trigger name cannot be same as model attribute name.")
         if trigger not in self.events:
             self.events[trigger] = self._create_event(trigger, self)
             for model in self.models:
diff --git a/transitions/extensions/diagrams.py b/transitions/extensions/diagrams.py
index aad0fbc..c73828f 100644
--- a/transitions/extensions/diagrams.py
+++ b/transitions/extensions/diagrams.py
@@ -164,7 +164,7 @@ class GraphMachine(MarkupMachine):
             grph = self.graph_cls(self, title=title if title is not None else self.title)
             self.model_graphs[model] = grph
             try:
-                self.model_graphs[model].set_node_style(model.state, 'active')
+                self.model_graphs[model].set_node_style(getattr(model, self.model_attribute), 'active')
             except AttributeError:
                 _LOGGER.info("Could not set active state of diagram")
         try:
@@ -172,7 +172,7 @@ class GraphMachine(MarkupMachine):
         except KeyError:
             _ = self._get_graph(model, title, force_new=True)
             m = self.model_graphs[model]
-        m.roi_state = model.state if show_roi else None
+        m.roi_state = getattr(model, self.model_attribute) if show_roi else None
         return m.get_graph(title=title)
 
     def get_combined_graph(self, title=None, force_new=False, show_roi=False):
diff --git a/transitions/extensions/markup.py b/transitions/extensions/markup.py
index de1e0c8..1a7f347 100644
--- a/transitions/extensions/markup.py
+++ b/transitions/extensions/markup.py
@@ -105,10 +105,10 @@ class MarkupMachine(Machine):
 
     def _convert_models(self):
         models = []
-        for m in self.models:
-            model_def = dict(state=m.state)
-            model_def['name'] = m.name if hasattr(m, 'name') else str(id(m))
-            model_def['class-name'] = 'self' if m == self else m.__module__ + "." + m.__class__.__name__
+        for model in self.models:
+            model_def = dict(state=getattr(model, self.model_attribute))
+            model_def['name'] = model.name if hasattr(model, 'name') else str(id(model))
+            model_def['class-name'] = 'self' if model == self else model.__module__ + "." + model.__class__.__name__
             models.append(model_def)
         return models
 
diff --git a/transitions/extensions/nesting.py b/transitions/extensions/nesting.py
index dc2f26d..f014d49 100644
--- a/transitions/extensions/nesting.py
+++ b/transitions/extensions/nesting.py
@@ -220,7 +220,7 @@ class NestedTransition(Transition):
         machine = event_data.machine
         model = event_data.model
         dest_state = machine.get_state(self.dest)
-        source_state = machine.get_state(model.state)
+        source_state = machine.get_model_state(model)
         lvl = source_state.exit_nested(event_data, dest_state)
         event_data.machine.set_state(self.dest, model)
         event_data.update(dest_state)
@@ -245,13 +245,13 @@ class NestedEvent(Event):
     """ An event type to work with nested states. """
 
     def _trigger(self, model, *args, **kwargs):
-        state = self.machine.get_state(model.state)
+        state = self.machine.get_model_state(model)
         while state.parent and state.name not in self.transitions:
             state = state.parent
         if state.name not in self.transitions:
             msg = "%sCan't trigger event %s from state %s!" % (self.machine.name, self.name,
-                                                               model.state)
-            if self.machine.get_state(model.state).ignore_invalid_triggers:
+                                                               self.machine.get_model_state(model))
+            if self.machine.get_model_state(model).ignore_invalid_triggers:
                 _LOGGER.warning(msg)
             else:
                 raise MachineError(msg)
@@ -319,9 +319,9 @@ class HierarchicalMachine(Machine):
 
         """
         if not allow_substates:
-            return model.state == state_name
+            return getattr(model, self.model_attribute) == state_name
 
-        return self.get_state(model.state).is_substate_of(state_name)
+        return self.get_model_state(model).is_substate_of(state_name)
 
     def _traverse(self, states, on_enter=None, on_exit=None,
                   ignore_invalid_triggers=None, parent=None, remap=None):
@@ -515,6 +515,6 @@ class HierarchicalMachine(Machine):
             state_name (str): Name of the destination state.
         """
 
-        event = EventData(self.get_state(model.state), Event('to', self), self,
+        event = EventData(self.get_model_state(model), Event('to', self), self,
                           model, args=args, kwargs=kwargs)
-        self._create_transition(model.state, state_name).execute(event)
+        self._create_transition(getattr(model, self.model_attribute), state_name).execute(event)

Ability to manage multiple states per Model;Custom state field for Model
First of all, thanks for this powerful lib :)

Currently the library makes an assumption that all Models will track the "state" using the `state` attribute. This assumption leads to two problems:

1. No ability to track the state changes in some other attribute/field.
2. A Model cannot have multiple states.

For example, If a use-case were to have two different fields on the Model. Both of them would represent different states and would be controlled by different Machines.

Something like this:
```python
lump = Matter()

lump.global_state
lump.pipeline_state

global_machine = Machine(lump, model_field='global_state', **kwargs)
pipeline_machine = Machine(lump, model_field='pipeline_state', **kwargs)
```

Is this out of scope of this library?
diff --git a/tests/test_core.py b/tests/test_core.py
index d6b726c..c4551c3 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1016,6 +1016,45 @@ class TestTransitions(TestCase):
         self.assertEqual(m.model.state, 'A')
         self.assertEqual(m.model.level, 2)
 
+    def test_dynamic_model_state_attribute(self):
+        class Model:
+            def __init__(self):
+                self.status = None
+                self.state = 'some_value'
+
+        m = Machine(Model(), states=['A', 'B'], initial='A', model_attribute='status')
+        self.assertEqual(m.model.status, 'A')
+        self.assertEqual(m.model.state, 'some_value')
+
+        m.add_transition('move', 'A', 'B')
+        m.model.move()
+
+        self.assertEqual(m.model.status, 'B')
+        self.assertEqual(m.model.state, 'some_value')
+
+    def test_multiple_machines_per_model(self):
+        class Model:
+            def __init__(self):
+                self.state_a = None
+                self.state_b = None
+
+        instance = Model()
+        machine_a = Machine(instance, states=['A', 'B'], initial='A', model_attribute='state_a')
+        machine_a.add_transition('melt', 'A', 'B')
+        machine_b = Machine(instance, states=['A', 'B'], initial='B', model_attribute='state_b')
+        machine_b.add_transition('freeze', 'B', 'A')
+
+        self.assertEqual(instance.state_a, 'A')
+        self.assertEqual(instance.state_b, 'B')
+
+        instance.melt()
+        self.assertEqual(instance.state_a, 'B')
+        self.assertEqual(instance.state_b, 'B')
+
+        instance.freeze()
+        self.assertEqual(instance.state_b, 'A')
+        self.assertEqual(instance.state_a, 'B')
+
 
 class TestWarnings(TestCase):
     def test_warning(self):

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-xdist pytest-mock pytest-asyncio pytest
apt-get update apt-get install -y gcc graphviz
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
coverage==6.2
execnet==1.9.0
importlib-metadata==4.8.3
iniconfig==1.1.1
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
pytest-asyncio==0.16.0
pytest-cov==4.0.0
pytest-mock==3.6.1
pytest-xdist==3.0.2
six==1.17.0
tomli==1.2.3
-e git+https://github.com/pytransitions/transitions.git@51087811b0e311903ee2a2fb022cdcd2a05f057f#egg=transitions
typing_extensions==4.1.1
zipp==3.6.0

name: transitions
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - coverage==6.2
      - execnet==1.9.0
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-asyncio==0.16.0
      - pytest-cov==4.0.0
      - pytest-mock==3.6.1
      - pytest-xdist==3.0.2
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/transitions

tests/test_core.py::TestTransitions::test_dynamic_model_state_attribute tests/test_core.py::TestTransitions::test_multiple_machines_per_model
tests/test_core.py::TestTransitions::test___getattr___and_identify_callback tests/test_core.py::TestTransitions::test_auto_transitions tests/test_core.py::TestTransitions::test_before_after_callback_addition tests/test_core.py::TestTransitions::test_before_after_transition_listeners tests/test_core.py::TestTransitions::test_callback_identification tests/test_core.py::TestTransitions::test_conditions tests/test_core.py::TestTransitions::test_conditions_with_partial tests/test_core.py::TestTransitions::test_dispatch tests/test_core.py::TestTransitions::test_function_callbacks tests/test_core.py::TestTransitions::test_get_transitions tests/test_core.py::TestTransitions::test_get_triggers tests/test_core.py::TestTransitions::test_ignore_invalid_triggers tests/test_core.py::TestTransitions::test_inheritance tests/test_core.py::TestTransitions::test_init_machine_with_hella_arguments tests/test_core.py::TestTransitions::test_internal_transition tests/test_core.py::TestTransitions::test_listify tests/test_core.py::TestTransitions::test_machine_finalize tests/test_core.py::TestTransitions::test_machine_finalize_exception tests/test_core.py::TestTransitions::test_machine_prepare tests/test_core.py::TestTransitions::test_multiple_add_transitions_from_state tests/test_core.py::TestTransitions::test_multiple_models tests/test_core.py::TestTransitions::test_ordered_transition_callback tests/test_core.py::TestTransitions::test_ordered_transition_condition tests/test_core.py::TestTransitions::test_ordered_transition_error tests/test_core.py::TestTransitions::test_ordered_transitions tests/test_core.py::TestTransitions::test_pass_state_instances_instead_of_names tests/test_core.py::TestTransitions::test_pickle tests/test_core.py::TestTransitions::test_pickle_model tests/test_core.py::TestTransitions::test_prep_ordered_arg tests/test_core.py::TestTransitions::test_prepare tests/test_core.py::TestTransitions::test_process_trigger tests/test_core.py::TestTransitions::test_property_initial tests/test_core.py::TestTransitions::test_queued tests/test_core.py::TestTransitions::test_queued_errors tests/test_core.py::TestTransitions::test_reflexive_transition tests/test_core.py::TestTransitions::test_remove_transition tests/test_core.py::TestTransitions::test_repr tests/test_core.py::TestTransitions::test_send_event_data_callbacks tests/test_core.py::TestTransitions::test_send_event_data_conditions tests/test_core.py::TestTransitions::test_skip_override tests/test_core.py::TestTransitions::test_state_and_transition_with_underscore tests/test_core.py::TestTransitions::test_state_callable_callbacks tests/test_core.py::TestTransitions::test_state_callbacks tests/test_core.py::TestTransitions::test_state_change_listeners tests/test_core.py::TestTransitions::test_state_model_change_listeners tests/test_core.py::TestTransitions::test_string_callbacks tests/test_core.py::TestTransitions::test_string_trigger tests/test_core.py::TestTransitions::test_transition_definitions tests/test_core.py::TestTransitions::test_transitioning tests/test_core.py::TestTransitions::test_use_machine_as_model tests/test_core.py::TestWarnings::test_warning
transitions/extensions/nesting.py transitions/core.py transitions/extensions/markup.py README.md transitions/extensions/diagrams.py
transitions/extensions/nesting.py transitions/core.py transitions/extensions/markup.py README.md transitions/extensions/diagrams.py