Skip to content
This repository has been archived by the owner on Apr 16, 2024. It is now read-only.

Implement "FIELD_change_state" methods #68

Merged
merged 2 commits into from
Dec 22, 2014
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,13 @@ Returns all transitions data available in current state
`get_available_user_FIELD_transitions`
Enumerates all transitions data available in current state for provided user

`FIELD_change_state`
Invoke a state changing by state value.
```python
post.state_change_state('publish')
post.state_change_state('destroy')
```

### Foreign Key constraints support

If you store the states in the db table you could use FSMKeyField to
Expand Down
15 changes: 15 additions & 0 deletions django_fsm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,20 @@ def get_available_user_FIELD_transitions(instance, user, field):
yield transition


def FIELD_change_state(field):
""" Run transaction by next state. """

@wraps(FIELD_change_state)
def wrapper(instance, state, *args, **kwargs):
targets = dict((tt.target, tt) for tt in instance.get_available_state_transitions())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's several transition functions for the target state, doesn't that select one of them randomly? If it is, I think we should change that and raise an exception instead.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, looks like initial idea was wrong suited for django-fsm

if state not in targets:
raise TransitionNotAllowed('Invalid transaction')
transition = targets[state]
field.change_state(instance, transition.method, *args, **kwargs)

return wrapper


class FSMMeta(object):
"""
Models methods transitions meta information
Expand Down Expand Up @@ -328,6 +342,7 @@ def contribute_to_class(self, cls, name, virtual_only=False):
curry(get_available_FIELD_transitions, field=self))
setattr(cls, 'get_available_user_{0}_transitions'.format(self.name),
curry(get_available_user_FIELD_transitions, field=self))
setattr(cls, '{0}_change_state'.format(self.name), FIELD_change_state(self))

class_prepared.connect(self._collect_transitions)

Expand Down
8 changes: 7 additions & 1 deletion django_fsm/tests/test_basic_transitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ def test_empty_string_target(self):
self.model.empty()
self.assertEqual(self.model.state, '')

def test_change_state_method(self):
self.model.state_change_state('published')
self.assertEqual(self.model.state, 'published')
with self.assertRaises(TransitionNotAllowed):
self.model.state_change_state('published')


class StateSignalsTests(TestCase):
def setUp(self):
Expand Down Expand Up @@ -128,7 +134,7 @@ def test_available_conditions(self):

def test_all_conditions(self):
transitions = self.model.get_all_state_transitions()

actual = set((transition.source, transition.target) for transition in transitions)
expected = set([('*', 'moderated'),
('new', 'published'),
Expand Down