Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions statemachine/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class MachineMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.state_machine_name:
if self._is_django_historical_model():
return
raise ValueError(
_("{!r} is not a valid state machine name.").format(self.state_machine_name)
)
Expand All @@ -34,3 +36,12 @@ def __init__(self, *args, **kwargs):
)
if self.bind_events_as_methods:
sm.bind_events_to(self)

@classmethod
def _is_django_historical_model(cls) -> bool:
"""Detect Django historical models created by ``apps.get_model()`` in migrations.

Django sets ``__module__ = '__fake__'`` on these dynamically-created classes,
which lack the user-defined class attributes like ``state_machine_name``.
"""
return getattr(cls, "__module__", None) == "__fake__"
15 changes: 15 additions & 0 deletions tests/test_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,18 @@ class MyModelWithoutMachineName(MachineMixin):

with pytest.raises(ValueError, match="None is not a valid state machine name"):
MyModelWithoutMachineName()


def test_mixin_should_skip_init_for_django_historical_models():
"""Regression test for #551: MachineMixin fails in Django data migrations.

Django's ``apps.get_model()`` returns historical models with ``__module__ = '__fake__'``
that don't carry user-defined class attributes like ``state_machine_name``.
"""

# Simulate a Django historical model: __module__ is '__fake__' and
# state_machine_name is not set (falls back to None from MachineMixin).
HistoricalModel = type("HistoricalModel", (MachineMixin,), {"__module__": "__fake__"})

instance = HistoricalModel()
assert not hasattr(instance, "statemachine")
Loading