viewflow / django-fsm Goto Github PK
View Code? Open in Web Editor NEWDjango friendly finite state machine support
License: MIT License
Django friendly finite state machine support
License: MIT License
Is it good or not?
http://schinckel.net/2013/06/13/django-proxy-model-state-machine/
Hi,
How can I handle an object state in the django admin?
All I can do is setting a 'choices' attribute in my field, to display a choice widget. However, it will not use the transitions methods.
How can I be sure to always use those methods?
Thanks.
If I use the latest version from pip (2.0.1) with Django 1.5.5, I encounter a TypeError when I try to define a field like this:
state = FSMField(default='new')
I also tried using the FSMIntegerField and I get the same error. I tried installing 1.6 from pip exactly as above (although the imports are different) and the above works.
Let me know if there's something I'm doing wrong, or there's anything else I can provide to help troubleshoot.
Hi there,
I have a few proxy models setup, but I can't figure out how to get the get_available_FIELD_transitions
thing to work.
class SubStep(models.Model):
STATE_START = 'start'
STATE_END = 'done'
state = FSMField(default=STATE_START)
class ApprovalStep(SubStep):
class Meta:
proxy = True
@transition(source='start', target='done')
def approve(self):
pass
If I do transition(field=state, ...)
in the proxy model, it can't seem to get a reference to the field in question. Do you have any suggestions for how I can accomplish this?
-justin
args and kwargs passed to side effect functions should also be passed to the pre_transition and post_transition signals so that that data can be used by the signal.
def can_proceed(bound_method, check_conditions=True):
"""
Returns True if model in state allows to call bound_method
Set ``check_conditions`` argument to ``False`` to skip checking
conditions.
"""
if not hasattr(bound_method, '_django_fsm'):
raise TypeError('%s method is not transition' % bound_method.im_func.__name__)
meta = bound_method._django_fsm
im_self = getattr(bound_method, 'im_self', getattr(bound_method, '__self__'))
current_state = meta.field.get_state(im_self)
return meta.has_transition(current_state) and (
not check_conditions or meta.conditions_met(im_self, current_state))
def has_transition_perm(bound_method, user):
"""
Returns True if model in state allows to call bound_method and user have rights on it
"""
if not hasattr(bound_method, '_django_fsm'):
raise TypeError('%s method is not transition' % bound_method.im_func.__name__)
meta = bound_method._django_fsm
im_self = getattr(bound_method, 'im_self', getattr(bound_method, '__self__'))
current_state = meta.field.get_state(im_self)
return (meta.has_transition(current_state)
and meta.conditions_met(im_self, current_state)
and meta.has_transition_perm(im_self, current_state, user))
Really nice project, I appreciate the effort put into the reliability and foolproof design, especially the protected option. From that I sense it was the author's desire to enforce FSM constrains on state transitions, so that no unwanted changes or executions happen. I second this intent, but want to point out one serious race condition that the current implementation suffers from: atomicity of transitions.
While it is very nice that the FSMField checks source and target states, this is not enforced on DB level. Multiple workers trying to manipulate the same object at the same time can cause serious issues, like one transition executing more than once (I have tested this and can prove it).
On the other hand, I understand the lightweight design of FSMField and realize that its current policy of not touching the DB whatsoever might be useful in many cases and thus is totally justified. At the same time though, it prevents developers from using this great app in any serious applications where race conditions cannot be neglected or overlooked.
I suggest one backward-compatible feature that would optionally enable DB integrity enforcement. It could be activated by an additional atomic=True parameter passed to FSMField (or FSMFieldMixin). When activated, all the field transitions would be executed in django.db.transaction.atomic()
block and the state would also be automatically changed, using an atomic ORM operation, e.g.
blog = BlogPost.objects.first()
blog.publish()
...would execute this in addition:
self._default_manager.filter(pk=self.pk, state='new').update(state='published')
The result of the update
operation would be checked and an exception raised if it did not match any object. This would cause the whole transaction inside the atomic() block to rollback, negating the effect of any persistent operations.
This is just an example; more sophisticated code would of course be used in real, but it covers the idea. I'm willing to implement this if you agree.
Cheers!
Joe
In the move to 2.0 it looks like the previous support for South was removed: 1ed0e2c
I think all that needs happen is those introspection rules get added back to the new django_fsm/__init__.py
and it should all work again.
Was the removal deliberate or would it be OK to have these back? I'm happy to make a quick pull request doing so if that's preferred.
Get rid of warnings
I'm getting this when running the graph_transitions
management command.
venv/lib/python2.7/site-packages/django_fsm_log/models.py:5: RemovedInDjango19Warning: django.contrib.contenttypes.generic is deprecated and will be removed in Django 1.9. Its contents have been moved to the fields, forms, and admin submodules of django.contrib.contenttypes.
from django.contrib.contenttypes.generic import GenericForeignKey
venv/lib/python2.7/site-packages/django_fsm_log/apps.py:14: RemovedInDjango19Warning: import_by_path() has been deprecated. Use import_string() instead.
backend = import_by_path(settings.DJANGO_FSM_LOG_STORAGE_METHOD)
venv/lib/python2.7/site-packages/django/core/management/base.py:259: RemovedInDjango19Warning: "requires_model_validation" is deprecated in favor of "requires_system_checks".
RemovedInDjango19Warning)
venv/lib/python2.7/site-packages/django_fsm/management/commands/graph_transitions.py:102: RemovedInDjango19Warning: django.db.models.get_model is deprecated.
model = get_model(field_spec[0], field_spec[1])
venv/python2.7/site-packages/django/db/models/__init__.py:55: RemovedInDjango19Warning: The utilities in django.db.models.loading are deprecated in favor of the new application loading system.
from . import loading
Traceback (most recent call last):
File "manage.py", line 10, in <module>
execute_from_command_line(sys.argv)
File "venv/lib/python2.7/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line
utility.execute()
File "venv/lib/python2.7/site-packages/django/core/management/__init__.py", line 330, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "venv/lib/python2.7/site-packages/django/core/management/base.py", line 390, in run_from_argv
self.execute(*args, **cmd_options)
File "venv/lib/python2.7/site-packages/django/core/management/base.py", line 441, in execute
output = self.handle(*args, **options)
File "venv/lib/python2.7/site-packages/django_fsm/management/commands/graph_transitions.py", line 112, in handle
dotdata = generate_dot(fields_data)
File "venv/lib/python2.7/site-packages/django_fsm/management/commands/graph_transitions.py", line 55, in generate_dot
subgraph.node(name, label=label, shape='doublecircle')
File "venv/lib/python2.7/site-packages/graphviz/dot.py", line 99, in node
attributes = self.attributes(label, attrs, _attributes)
File "venv/lib/python2.7/site-packages/graphviz/lang.py", line 91, in attributes
result = ['label=%s' % quote(label)]
File "venv/lib/python2.7/site-packages/graphviz/lang.py", line 44, in quote
if html(identifier):
TypeError: expected string or buffer
I believe transitions could be implemented as context managers for use with 'with' statement ( https://www.python.org/dev/peps/pep-0343/ ).
This will allow to easily implement state changes outside of the model class and with proper exception handling (setting on_error state if exception occurs).
when calling refresh_from_db on a model that has an FSMField with protected=True, i get the following trace:
Traceback (most recent call last):
File "/vagrant/evap/staff/tests.py", line 284, in test_has_enough_questionnaires
course.refresh_from_db()
File "/usr/local/lib/python3.4/dist-packages/django/db/models/base.py", line 627, in refresh_from_db
setattr(self, field.attname, getattr(db_instance, field.attname))
File "/usr/local/lib/python3.4/dist-packages/django_fsm/__init__.py", line 210, in __set__
raise AttributeError('Direct {0} modification is not allowed'.format(self.field.name))
AttributeError: Direct state modification is not allowed
in case you can't fix that at all with reasonably clean code, this might at least be worth a note in the docs.
@transition(field=state, source=['ready','wait'])
def command_spin(self, args={}):
if ... :
self.state='error'
else:
self.state='process'
self.save()
Exception: File " ... /lib/python2.7/site-packages/django_fsm/db/fields/fsmfield.py", line 73, in next_state
result = self.transitions['']
KeyError: ''
Currently the *
shortcut can be used to specify that a transition can happen from any state.
It would be great if there was a simple shortcut that allowed a transition from any state except the target. For example, if I have a state called 'Publish' then it doesn't make sense to transition from 'Publish' to 'Publish'.
Possible syntax:
from django_fsm import ALL_BUT_TARGET
@transition(field=state, source=ALL_BUT_TARGET, target='published')
def publish(self):
pass
Neec some support for hierarhical and orthonogal states for multiple state fields
http://en.wikipedia.org/wiki/UML_state_machine
.
At the moment, FSM field descriptors work in either of two modes: protected, in which setting the state raises an exception, and unprotected, in which setting the state bypasses all transitions.
I was wondering whether an additional mode could be added, in which setting the state would trigger a transition, using change_state
with no optional arguments. I know I can do that easily by inheriting FSMFieldMixin
and setting a custom descriptor_class
, but direct support by fsm would be much cleaner.
Maybe it would be sensible to make TransitionNotAllowed
a subclass of ValueError
.
(Great module by the way, abstracting out this common pattern comes in really handy).
I was experiencing the problem in #77 and realized that it's because the labels in my choices
for my FSMInteger field are wrapped with ugettext_lazy. Thus graphviz is getting a django Promise instead of a string.
In order to allow override methods
# in inherited class
@transition(...)
def out_transition(self):
super(...).our_transition.original()
When I use Sphinx doc coverage build, I get the following error:
Traceback (most recent call last):
File "/home/polesz/dem/venv/lib/python2.7/site-packages/sphinx/cmdline.py", line 245, in main
app.build(opts.force_all, filenames)
File "/home/polesz/dem/venv/lib/python2.7/site-packages/sphinx/application.py", line 264, in build
self.builder.build_update()
File "/home/polesz/dem/venv/lib/python2.7/site-packages/sphinx/builders/__init__.py", line 240, in build_update
self.build(['__all__'], to_build)
File "/home/polesz/dem/venv/lib/python2.7/site-packages/sphinx/builders/__init__.py", line 316, in build
self.write(docnames, list(updated_docnames), method)
File "/home/polesz/dem/venv/lib/python2.7/site-packages/sphinx/ext/coverage.py", line 77, in write
self.build_py_coverage()
File "/home/polesz/dem/venv/lib/python2.7/site-packages/sphinx/ext/coverage.py", line 190, in build_py_coverage
attr = getattr(obj, attr_name)
File "/home/polesz/dem/venv/lib/python2.7/site-packages/django_fsm/__init__.py", line 205, in __get__
raise AttributeError('Can only be accessed via an instance.')
AttributeError: Can only be accessed via an instance.
After some debugging it turned out that coverage.py
gets the attributes of my model class, not an actual object. Adding an
import types
at the beginning of __init__.py
, and a changing the beginning of __get__()
like this (note that I changed the type
parameter to obj_type
as type
is a reserved word):
def __get__(self, instance, obj_type=None):
if isinstance(obj_type, (type, types.ClassType)):
return
…
fixes the problem (although seems to be a bit ugly).
Hello,
I'm trying to use Django FSM and Django FSM Admin but I get the error:
'Employee' object has no attribute 'get_available_user_state_transitions'
In template C:\Python27\lib\site-packages\fsm_admin\templates\fsm_admin\change_form.html, error at line 4: {% fsm_submit_row %}
The last traceback is:
transitions = getattr(obj, transitions_func)(user) if obj else []
with transitions_func = u'get_available_user_state_transitions'
(in Python27\lib\site-packages\fsm_admin\mixins.py in _fsm_get_transitions)
I don't understand...
in models.py:
class Employee(models.Model):
step = FSMField(default=STEP_1, choices=CHOICES, protected=True)
...
@transition(field=step, source=STEP_1, target=STEP_2)
def step1to2(self):
pass
in admin.py:
@admin.register(Employee)
class EmployeeAdmin(FSMTransitionMixin, reversion.VersionAdmin, admin.ModelAdmin):
...
in settings.py:
INSTALLED_APPS = (
'suit',
'django.contrib.admin',
'django_fsm',
'fsm_admin',
...
Hi,
I've already used fsm with CharField based stats (FSMField instance)
For a new project, I would like to use fsm with a FSMKeyField, but it doesn't work for me...
AttributeError: 'FSMKeyField' object has no attribute 'transitions'
Extract from my pip freeze :
Django==1.6.1
django-fsm==1.5.1
Thanks for your work !
Hi,
I implemented a dirty approach to asking a Model() what transitions/states are available from it's current state, implemented in https://github.com/bradwhittington/django-fsm / bradwhittington/django-fsm@ea5d7d14f5abed5d040d9b2184bda80f6b47d1f8 (minor fixes in bradwhittington/django-fsm@262c171f777588c2c0398dbb2377a92f159c35f8
Would like to hear your thoughts on better approaches, and if you feel it even belongs where I have put it.
An example:
In [1]: from task import models
In [2]: u=models.User.objects.get()
In [3]: m=models.Task.objects.get()
In [4]: m.get_available_status_transitions()
Out[4]:
[('clarification',
<bound method Task.get_clarification of <Task: Task object>>),
('assigned', <bound method Task.reassign of <Task: Task object>>),
('in-progress', <bound method Task.start_work of <Task: Task object>>),
('unassigned', <bound method Task.unassign of <Task: Task object>>)]
In [5]: m.unassign()
In [6]: m.get_available_status_transitions()
Out[6]:
[('assigned', <bound method Task.assign of <Task: Task object>>),
('clarification',
<bound method Task.get_clarification of <Task: Task object>>)]
In [7]: m.assign(u)
In [8]: m.get_available_status_transitions()
Out[8]:
[('clarification',
<bound method Task.get_clarification of <Task: Task object>>),
('assigned', <bound method Task.reassign of <Task: Task object>>),
('in-progress', <bound method Task.start_work of <Task: Task object>>),
('unassigned', <bound method Task.unassign of <Task: Task object>>)]
There was several tries to add parameters passing to trahsition conditions. And every time pull requesters clain the only one use case for that - user permission checking.
Doing that in conditions is basically wrong. And it makes get_all_available_transition method ugly. B/c you can't mix conditions with user param and without, or have to use **kwargs everywhere.
At the end if user have no permission TransitionNotAllowed would be raisen that makes no sence.
The right solution should allow to make model transition without user (in case of background celery task or admin console when user are not available) And checking user permission should raise PermissionDenied exception.
So, i think right solution should be follows:
permission
arg to transition method that accepts django permission string name or callable that accepts user @transition(field=status, source='NEW', target='PENDING',
permission='myapp.can_start_entry')
def start(self):
pass
Usage
def start(request, entry_pk):
entry = get_object_or_404(Entry, pk=entry_pk)
if not enty.start.has_perm(request.user):
raise PermissionDenied
if request.POST:
entry.start()
entry.save()
return redirect('/')
return render(request, {'entry': entry})
If you have a model with a FSMKeyField state like this one of the README:
class DbState(models.Model):
id = models.CharField(primary_key=True, max_length=50)
label = models.CharField(max_length=255)
def __unicode__(self);
return self.label
class BlogPost(models.Model):
state = FSMKeyField(DbState, default='new')
@transition(field=state, source='new', target='published')
def publish(self):
pass
And then you call the decorated publish()
method on a BlogPost instance, it will raise this exception:
"TransitionNotAllowed: Can't switch from state 'new' using method 'publish'"
Reading the code it seems that the has_transition
method of the FSMMeta
is looking for the current state in its transitions
property, but the current state actually is an instance of DbState
, and transitions are strings, so this method will always cast to False causing the validation to fail and raise the exception.
No need to repeat @transition
decorator parameters again
https://github.com/viewflow/viewflow/blob/master/tests/unit/tests/test_fsm.py#L22
From the changelogs it looks like having having multiple transition decorators for a same method is supported:
Correct support for several @transitions decorator with different source states and conditions on same method
However, it looks like it has a side effect on the signals (triggered as many times as number of decorators).
###############################################
######## In your models.py #############
###############################################
from django.db import models
from django_fsm import FSMField, transition
from django_fsm.signals import post_transition
class PostStateMachine(models.Model):
state = FSMField(default="CREATED")
@transition(field=state, source="CREATED", target="SUBMITTED_BY_USER")
def submit_as_user(self):
pass
@transition(field=state, source="CREATED", target="SUBMITTED_BY_ADMIN")
def submit_as_admin(self):
pass
@transition(field=state, source="CREATED", target="SUBMITTED_BY_ANONYMOUS")
def submit_as_anonymous(self):
pass
@transition(field=state, source="SUBMITTED_BY_USER", target="REVIEW_USER")
@transition(field=state, source="SUBMITTED_BY_ADMIN", target="REVIEW_ADMIN")
@transition(field=state, source="SUBMITTED_BY_ANONYMOUS", target="REVIEW_ANONYMOUS")
def review(self):
pass
def count_calls(sender, instance, name, source, target, **kwargs):
if name == "review":
print name, source, target # You will see 3 print statements
post_transition.connect(count_calls, sender=PostStateMachine)
###############################################
######## In your tests #############
###############################################
psm = PostStateMachine.objects.create()
psm.submit_as_admin() # No print
psm.review() # 3 prints
It would be nice if transitions optionally wrapped in a transaction, so that the actions taken to transition, and any side-effects in the decorated method, are atomic.
I have a class that inherits its state field from a base class. This means that the FSMField doesn't exist in the class itself, and so I'm getting the "Non explicid (sic) field transition support going to be removed" deprecation warning.
The decorator should take inheritance into account?
Would it make more sense to set state fields to readonly in django admin when protected=True?
Wouldn't it make sense to allow something such as:
@transition(..., name=_("translatable transitionname"))
and only if name is missing, then fall back to transition.method.__name__
In django-fsm-admin this name then could be used in multilingual environments. Currently the name is derived from the method name, which does not always give satisfying results.
There is several tries that introduce explicit state transition in case of exeption:
https://gist.github.com/rca/7421319
#31
Need to select right approach and impelement it including graph support
Specifically, the model does not have a CharField as the primary key, it uses Django's default integer field.
@transition(field=state, source='APPROVED', target='REVIEW')
@transition(field=submission_state, source='DONE', target='WAITING')
def review(self):
pass
You use a README.md
file, which is good. PyPI however expects text in RST format. I also do this, but in my projects setup.py
I added this line
def read(fname):
readme_file = os.path.join(os.path.dirname(__file__), fname)
return os.popen('[ -x "$(which pandoc 2>/dev/null)" ] && pandoc -t rst {0} || cat {0}'.format(readme_file)).read()
setup(
...
long_description=read('README.md'),
...
)
Of course you have to install pandoc, but thats worth the trouble.
Have a look here: https://pypi.python.org/pypi/django-angular , it is generated by pandoc.
I just upgraded from django-fsm 1.6 to 2.1 and now get this error when trying to run any management command:
CommandError: One or more models did not validate:
anx.dimensioncre: 'cre' has a relation with model portal.Cre, which has either not been installed or is abstract.
The only change I made was upgrading from django-fsm 1.6. The changelog says something about the release being backwards incompatible -- but doesn't document an upgrade procedure.
I'm running Django 1.6.5.
I get the error
"session state instance with id SessionState: TEST_1 does not exist."
whenever adding a model that uses the FSMKeyField to reference a state that has a CAPITAL character in its label.
The KeyField works fine if it is replaced with all lowercase letters, ie "test_1" works fine.
Mixins with @transition decorator
the conditions
parameter currently must be a list of callables, that take a model instance as argument.
suppose the model has a boolean property, called is_happy
. to make is_happy
a condition for some transition, i would have to write some method is_happy_func(self): return self.is_happy
or put some lambda into the conditions, which doesn't look cool.
the same problem arises when the model has a lot of functions that are @property
s, e.g. because the project guidelines say that all methods that begin with is_
, has_
or can_
should be properties (like in my case).
in case the current syntax does not work: one approach would be to allow strings in the conditions list that are used to "look up" properties on the model instance. that might be a bit weird at first sight but django uses such string parameters extensively e.g. in the QuerySet API, so django-fsm users should be familiar with that.
In the README.md file there is an example using transition_type='manual'
, but I can't find any information telling what this means.
Any explanation?
Edit: I needed to include the choices kwarg to FSMIntegerField
. Perhaps it would be a good idea to print an error message when this happens rather than failing with an exception?
Original:
Hello,
I have this model that simulates a QA process using django-fsm:
class QAState(enum.IntEnum):
NEW = 1
TECH_QA = 2
COMPLETED = 5
class QAMixin(ConcurrentTransitionMixin, models.Model):
qa_state = FSMIntegerField(default=QAState.NEW, protected=True)
@transition(qa_state, source=QAState.NEW, target=QAState.TECH_QA)
def send_for_tech_qa(self, user: User):
pass
@transition(qa_state, source=QAState.TECH_QA, target=QAState.NEW)
def failed_tech_qa(self, user: User):
pass
@transition(qa_state, source=QAState.TECH_QA, target=QAState.COMPLETED)
def passed_tech_qa(self, user: User):
pass
class Model:
abstract = True
Whenever I try to run graph_transitions
the following error occurs:
Traceback (most recent call last):
File "/home/vagrant/.pycharm_helpers/pycharm/django_manage.py", line 41, in <module>
run_module(manage_file, None, '__main__', True)
File "/usr/lib/python3.4/runpy.py", line 182, in run_module
return _run_module_code(code, init_globals, run_name, mod_spec)
File "/usr/lib/python3.4/runpy.py", line 96, in _run_module_code
mod_name, mod_spec, pkg_name, script_name)
File "/usr/lib/python3.4/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/home/doge/manage.py", line 11, in <module>
execute_from_command_line(sys.argv)
File "/home/vagrant/venv/lib/python3.4/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line
utility.execute()
File "/home/vagrant/venv/lib/python3.4/site-packages/django/core/management/__init__.py", line 330, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/home/vagrant/venv/lib/python3.4/site-packages/django/core/management/base.py", line 393, in run_from_argv
self.execute(*args, **cmd_options)
File "/home/vagrant/venv/lib/python3.4/site-packages/raven/contrib/django/management/__init__.py", line 41, in new_execute
return original_func(self, *args, **kwargs)
File "/home/vagrant/venv/lib/python3.4/site-packages/django/core/management/base.py", line 444, in execute
output = self.handle(*args, **options)
File "/home/vagrant/venv/lib/python3.4/site-packages/django_fsm/management/commands/graph_transitions.py", line 161, in handle
dotdata = generate_dot(fields_data)
File "/home/vagrant/venv/lib/python3.4/site-packages/django_fsm/management/commands/graph_transitions.py", line 49, in generate_dot
source_label = [smart_text(name[1]) for name in field.choices if name[0] == transition.source][0]
IndexError: list index out of range
This doesn't happen if there are no transitions on the model, but as far as I can tell the QAMixin is working? I've tested the transitions themselves and that all works?
For improved debuggability / exception catching, I suggest that TransitionNotAllowedMethod could be instanciated with an optional reference to object and transition method as kwargs and keeps them as attributes.
I spent some time debugging a state transition issue where everything seemed to b correct, except the state is not updated after executing the handler.
It seems the issue is with the target state value being 0. In this case the state transition is not performed. Changing the target state value to anything different than 0 fixes this.
Is this expected behavior? Looks like a bug to me.
I looked through the history and it looks like this used to be a feature. Why was it removed? I would like to use something like this for my own project.
@transition(... save=True)
Thanks for providing a nice library for me to use! :)
Hi,
First, thank you for this nice piece of software.
One thing is bugging me though: how can I set a readable, translated attribute for the state values?
If my object is in the 'new' state, I have to display 'Nouveau' in my frontend. Is there an easy way to do this? If no, what would be the easiest way to add it?
Thanks.
Add Model.method.incoming_states() shortcut.
Usecase
for blog in Blog.objects.filter(Q(**Blog.publish.incoming_states())):
blog.publish()
I am modeling requests against a resource, where some resources require requests to be approved and others don't. I'd like my request creation logic (in a view) to be unaware of this, and have just a single method to send a request.
class Request(models.Model):
resource = models.ForiegnKey(Resource)
# intentionally no default -- must be request()ed to create
state = FSMField()
@transition(field=state, source=None, target=REQUESTED)
def request(self):
if self.resource.approval_required:
send_approval_request()
else:
# this obviously doesn't work -- state will get overwritten to
# REQUESTED once we return.
self.state = APPROVED
I can work around this by making request not actually a transition, but I have to duplicate the checking logic that a transition would have automatically:
def request(self):
assert self.state == None, "Can only send a request once. Why am I checking this instead of django-fsm?"
if self.resource.approval_required:
send_approval_request()
self.state = REQUESTED
else:
self.state = APPROVED
It would be nice for a transition method to be able to decide the next state to choose based on something external to the state machine, like a resource's configuration.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.