{ "info": { "author": "Antoine Fontaine", "author_email": "antoine.fontaine@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 1.11", "Framework :: Django :: 2.0", "Framework :: Django :: 2.1", "Framework :: Django :: 2.2", "Framework :: Django :: 3.0", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "Django friendly finite state machine support\n============================================\n\nThis is a friendly fork which aims to ensure that the tests are passing with\nthe new python and django versions, the original django-fsm was written by\nMikhail Podgurskiy (kmmbvnr@gmail.com), https://github.com/viewflow/django-fsm\n\n|Build Status| |Pypi Status|\n\ndjango-fsm adds simple declarative states management for django models.\n\nIf you need parallel task execution, view and background task code reuse\nover different flows - check my new project django-viewflow:\n\nhttps://github.com/viewflow/viewflow\n\n\nInstead of adding some state field to a django model, and managing its\nvalues by hand, you could use FSMState field and mark model methods with\nthe ``transition`` decorator. Your method could contain the side-effects\nof the state change.\n\nNice introduction is available here:\nhttps://gist.github.com/Nagyman/9502133\n\nYou may also take a look at django-fsm-admin project containing a mixin\nand template tags to integrate django-fsm state transitions into the\ndjango admin.\n\nhttps://github.com/gadventures/django-fsm-admin\n\nTransition logging support could be achived with help of django-fsm-log\npackage\n\nhttps://github.com/gizmag/django-fsm-log\n\nFSM really helps to structure the code, especially when a new developer\ncomes to the project. FSM is most effective when you use it for some\nsequential steps.\n\n\nInstallation\n------------\n\n.. code:: bash\n\n $ pip install django-fsm\n\nOr, for the latest git version\n\n.. code:: bash\n\n $ pip install -e git://github.com/kmmbvnr/django-fsm.git#egg=django-fsm\n\nThe library has full Python 3 support\n\nUsage\n-----\n\nAdd FSMState field to your model\n\n.. code:: python\n\n from django_fsm import FSMField, transition\n\n class BlogPost(models.Model):\n state = FSMField(default='new')\n\nUse the ``transition`` decorator to annotate model methods\n\n.. code:: python\n\n @transition(field=state, source='new', target='published')\n def publish(self):\n \"\"\"\n This function may contain side-effects,\n like updating caches, notifying users, etc.\n The return value will be discarded.\n \"\"\"\n\n``source`` parameter accepts a list of states, or an individual state.\nYou can use ``*`` for source, to allow switching to ``target`` from any\nstate. The ``field`` parameter accepts both a string attribute name or an\nactual field instance.\n\nIf calling publish() succeeds without raising an exception, the state\nfield will be changed, but not written to the database.\n\n.. code:: python\n\n from django_fsm import can_proceed\n\n def publish_view(request, post_id):\n post = get_object__or_404(BlogPost, pk=post_id)\n if not can_proceed(post.publish):\n raise PermissionDenied\n\n post.publish()\n post.save()\n return redirect('/')\n\nIf some conditions are required to be met before changing the state, use\nthe ``conditions`` argument to ``transition``. ``conditions`` must be a\nlist of functions taking one argument, the model instance. The function\nmust return either ``True`` or ``False`` or a value that evaluates to\n``True`` or ``False``. If all functions return ``True``, all conditions\nare considered to be met and the transition is allowed to happen. If one\nof the functions returns ``False``, the transition will not happen.\nThese functions should not have any side effects.\n\nYou can use ordinary functions\n\n.. code:: python\n\n def can_publish(instance):\n # No publishing after 17 hours\n if datetime.datetime.now().hour > 17:\n return False\n return True\n\nOr model methods\n\n.. code:: python\n\n def can_destroy(self):\n return self.is_under_investigation()\n\nUse the conditions like this:\n\n.. code:: python\n\n @transition(field=state, source='new', target='published', conditions=[can_publish])\n def publish(self):\n \"\"\"\n Side effects galore\n \"\"\"\n\n @transition(field=state, source='*', target='destroyed', conditions=[can_destroy])\n def destroy(self):\n \"\"\"\n Side effects galore\n \"\"\"\n\nYou could instantiate a field with protected=True option, that prevents\ndirect state field modification.\n\n.. code:: python\n\n class BlogPost(models.Model):\n state = FSMField(default='new', protected=True)\n\n model = BlogPost()\n model.state = 'invalid' # Raises AttributeError\n\nNote that calling\n`refresh_from_db `_\non a model instance with a protected FSMField will cause an exception.\n\n`target`\n~~~~~~~~\n\n`target` state parameter could point to a specific state or `django_fsm.State` implementation\n\n.. code:: python\n \n from django_fsm import FSMField, transition, RETURN_VALUE, GET_STATE\n @transition(field=state,\n source='*',\n target=RETURN_VALUE('for_moderators', 'published'))\n def publish(self, is_public=False):\n return 'for_moderators' if is_public else 'published'\n\n @transition(\n field=state,\n source='for_moderators',\n target=GET_STATE(\n lambda self, allowed: 'published' if allowed else 'rejected',\n states=['published', 'rejected']))\n def moderate(self, allowed):\n self.allowed=allowed\n\n\n``custom`` properties\n~~~~~~~~~~~~~~~~~~~~~\n\nCustom properties can be added by providing a dictionary to the\n``custom`` keyword on the ``transition`` decorator.\n\n.. code:: python\n\n @transition(field=state,\n source='*',\n target='onhold',\n custom=dict(verbose='Hold for legal reasons'))\n def legal_hold(self):\n \"\"\"\n Side effects galore\n \"\"\"\n\n``on_error`` state\n~~~~~~~~~~~~~~~~~~\n\nIn case of transition method would raise exception, you can provide\nspecific target state\n\n.. code:: python\n\n @transition(field=state, source='new', target='published', on_error='failed')\n def publish(self):\n \"\"\"\n Some exception could happen here\n \"\"\"\n\n``state_choices``\n~~~~~~~~~~~~~~~~~\n\nInstead of passing two elements list ``choices`` you could use three\nelements ``state_choices``, the last element states for string reference\nto model proxy class.\n\nBase class instance would be dynamically changed to corresponding Proxy\nclass instance, depending on the state. Even for queryset results, you\nwill get Proxy class instances, even if QuerySet executed on base class.\n\nCheck the `test\ncase `__\nfor example usage. Or read about `implementation\ninternals `__\n\nPermissions\n~~~~~~~~~~~\n\nIt is common to have permissions attached to each model transition.\n``django-fsm`` handles this with ``permission`` keyword on the\n``transition`` decorator. ``permission`` accepts a permission string, or\ncallable that expects ``instance`` and ``user`` arguments and returns\nTrue if user can perform the transition.\n\n.. code:: python\n\n @transition(field=state, source='*', target='publish',\n permission=lambda instance, user: not user.has_perm('myapp.can_make_mistakes'))\n def publish(self):\n pass\n\n @transition(field=state, source='*', target='publish',\n permission='myapp.can_remove_post')\n def remove(self):\n pass\n\nYou can check permission with ``has_transition_permission`` method\n\n.. code:: python\n\n from django_fsm import has_transition_perm\n def publish_view(request, post_id):\n post = get_object_or_404(BlogPost, pk=post_id)\n if not has_transition_perm(post.publish, request.user):\n raise PermissionDenied\n\n post.publish()\n post.save()\n return redirect('/')\n\nModel methods\n~~~~~~~~~~~~~\n\n``get_all_FIELD_transitions`` Enumerates all declared transitions\n\n``get_available_FIELD_transitions`` Returns all transitions data\navailable in current state\n\n``get_available_user_FIELD_transitions`` Enumerates all transitions data\navailable in current state for provided user\n\nForeign Key constraints support\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf you store the states in the db table you could use FSMKeyField to\nensure Foreign Key database integrity.\n\nIn your model :\n\n.. code:: python\n\n class DbState(models.Model):\n id = models.CharField(primary_key=True, max_length=50)\n label = models.CharField(max_length=255)\n\n def __unicode__(self):\n return self.label\n\n\n class BlogPost(models.Model):\n state = FSMKeyField(DbState, default='new')\n\n @transition(field=state, source='new', target='published')\n def publish(self):\n pass\n\nIn your fixtures/initial\\_data.json :\n\n.. code:: json\n\n [\n {\n \"pk\": \"new\",\n \"model\": \"myapp.dbstate\",\n \"fields\": {\n \"label\": \"_NEW_\"\n }\n },\n {\n \"pk\": \"published\",\n \"model\": \"myapp.dbstate\",\n \"fields\": {\n \"label\": \"_PUBLISHED_\"\n }\n }\n ]\n\nNote : source and target parameters in @transition decorator use pk\nvalues of DBState model as names, even if field \"real\" name is used,\nwithout \\_id postfix, as field parameter.\n\nInteger Field support\n~~~~~~~~~~~~~~~~~~~~~\n\nYou can also use ``FSMIntegerField``. This is handy when you want to use\nenum style constants.\n\n.. code:: python\n\n class BlogPostStateEnum(object):\n NEW = 10\n PUBLISHED = 20\n HIDDEN = 30\n\n class BlogPostWithIntegerField(models.Model):\n state = FSMIntegerField(default=BlogPostStateEnum.NEW)\n\n @transition(field=state, source=BlogPostStateEnum.NEW, target=BlogPostStateEnum.PUBLISHED)\n def publish(self):\n pass\n\nSignals\n~~~~~~~\n\n``django_fsm.signals.pre_transition`` and\n``django_fsm.signals.post_transition`` are called before and after\nallowed transition. No signals on invalid transition are called.\n\nArguments sent with these signals:\n\n**sender** The model class.\n\n**instance** The actual instance being proceed\n\n**name** Transition name\n\n**source** Source model state\n\n**target** Target model state\n\nOptimistic locking\n------------------\n\n``django-fsm`` provides optimistic locking mixin, to avoid concurrent\nmodel state changes. If model state was changed in database\n``django_fsm.ConcurrentTransition`` exception would be raised on\nmodel.save()\n\n.. code:: python\n\n from django_fsm import FSMField, ConcurrentTransitionMixin\n\n class BlogPost(ConcurrentTransitionMixin, models.Model):\n state = FSMField(default='new')\n\nFor guaranteed protection against race conditions caused by concurrently\nexecuted transitions, make sure:\n\n- Your transitions do not have any side effects except for changes in the database,\n- You always run the save() method on the object within ``django.db.transaction.atomic()`` block.\n\nFollowing these recommendations, you can rely on\nConcurrentTransitionMixin to cause a rollback of all the changes that\nhave been executed in an inconsistent (out of sync) state, thus\npractically negating their effect.\n\nDrawing transitions\n-------------------\n\nRenders a graphical overview of your models states transitions\n\nYou need ``pip install graphviz>=0.4`` library and add ``django_fsm`` to\nyour ``INSTALLED_APPS``:\n\n.. code:: python\n\n INSTALLED_APPS = (\n ...\n 'django_fsm',\n ...\n )\n\n.. code:: bash\n\n # Create a dot file\n $ ./manage.py graph_transitions > transitions.dot\n\n # Create a PNG image file only for specific model\n $ ./manage.py graph_transitions -o blog_transitions.png myapp.Blog\n\n.. |Build Status| image:: https://travis-ci.org/MDziwny/django-fsm.svg?branch=develop\n :target: https://travis-ci.org/MDziwny/django-fsm\n.. |Pypi Status| image:: https://badge.fury.io/py/django-fsa.svg\n :target: https://badge.fury.io/py/django-fsa\n\nChangelog\n=========\n\ndjango-fsm 2.8.0 2019-12-08\n---------------------------\n\n- Add compatibility tests with Django 3.0\n- Remove the dependency with django-guardians in the tests\n- change some django deprecated functions\n\n\ndjango-fsm 2.7.2 2019-05-01\n---------------------------\n\n- Add compatibility test with python 3.8\n- Add compatibility test with Django 2.2\n - Refactor the models in tests, a migration\n\ndjango-fsm 2.7.1 2019-03-04\n---------------------------\n\n- Delivery on Pypi\n\ndjango-fsm 2.7.0 2018-12-14\n---------------------------\n\n- Fix the travis tests\n- Add compatibility test with python 3.7, remove 2.6 and 3.3\n- Add compatibility test with Django 2.0 and 2.1, remove 1.6, 1.8, 1.9 and 1.10\n\ndjango-fsm 2.6.0 2017-06-08\n---------------------------\n\n- Fix django 1.11 compatibility\n- Fix TypeError in `graph_transitions` command when using django's lazy translations\n\n\ndjango-fsm 2.5.0 2017-03-04\n---------------------------\n\n- graph_transition command fix for django 1.10\n- graph_transition command supports GET_STATE targets\n- signal data extended with method args/kwargs and field\n- sets allowed to be passed to the transition decorator\n\n\ndjango-fsm 2.4.0 2016-05-14\n---------------------------\n\n- graph_transition commnad now works with multiple FSM's per model\n- Add ability to set target state from transition return value or callable\n\n\ndjango-fsm 2.3.0 2015-10-15\n---------------------------\n\n- Add source state shortcut '+' to specify transitions from all states except the target\n- Add object-level permission checks\n- Fix translated labels for graph of FSMIntegerField\n- Fix multiple signals for several transition decorators\n\n\ndjango-fsm 2.2.1 2015-04-27\n---------------------------\n\n- Improved exception message for unmet transition conditions.\n- Don't send post transition signal in case of no state changes on\n exception\n- Allow empty string as correct state value\n- Improved graphviz fsm visualisation\n- Clean django 1.8 warnings\n\ndjango-fsm 2.2.0 2014-09-03\n---------------------------\n\n- Support for `class\n substitution `__\n to proxy classes depending on the state\n- Added ConcurrentTransitionMixin with optimistic locking support\n- Default db\\_index=True for FSMIntegerField removed\n- Graph transition code migrated to new graphviz library with python 3\n support\n- Ability to change state on transition exception\n\ndjango-fsm 2.1.0 2014-05-15\n---------------------------\n\n- Support for attaching permission checks on model transitions\n\ndjango-fsm 2.0.0 2014-03-15\n---------------------------\n\n- Backward incompatible release\n- All public code import moved directly to django\\_fsm package\n- Correct support for several @transitions decorator with different\n source states and conditions on same method\n- save parameter from transition decorator removed\n- get\\_available\\_FIELD\\_transitions return Transition data object\n instead of tuple\n- Models got get\\_available\\_FIELD\\_transitions, even if field\n specified as string reference\n- New get\\_all\\_FIELD\\_transitions method contributed to class\n\ndjango-fsm 1.6.0 2014-03-15\n---------------------------\n\n- FSMIntegerField and FSMKeyField support\n\ndjango-fsm 1.5.1 2014-01-04\n---------------------------\n\n- Ad-hoc support for state fields from proxy and inherited models\n\ndjango-fsm 1.5.0 2013-09-17\n---------------------------\n\n- Python 3 compatibility\n\ndjango-fsm 1.4.0 2011-12-21\n---------------------------\n\n- Add graph\\_transition command for drawing state transition picture\n\ndjango-fsm 1.3.0 2011-07-28\n---------------------------\n\n- Add direct field modification protection\n\ndjango-fsm 1.2.0 2011-03-23\n---------------------------\n\n- Add pre\\_transition and post\\_transition signals\n\ndjango-fsm 1.1.0 2011-02-22\n---------------------------\n\n- Add support for transition conditions\n- Allow multiple FSMField in one model\n- Contribute get\\_available\\_FIELD\\_transitions for model class\n\ndjango-fsm 1.0.0 2010-10-12\n---------------------------\n\n- Initial public release\n\ncopyright (c) 2010 Mikhail Podgurskiy\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.", "description_content_type": "text/x-rst", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/mdziwny/django-fsm", "keywords": "django", "license": "MIT License", "maintainer": "", "maintainer_email": "", "name": "django-fsa", "package_url": "https://pypi.org/project/django-fsa/", "platform": "any", "project_url": "https://pypi.org/project/django-fsa/", "project_urls": { "Homepage": "http://github.com/mdziwny/django-fsm" }, "release_url": "https://pypi.org/project/django-fsa/2.8.0/", "requires_dist": null, "requires_python": "", "summary": "Django friendly finite state machine support, forked from django-fsm", "version": "2.8.0", "yanked": false, "yanked_reason": null }, "last_serial": 6262748, "releases": { "2.7.1": [ { "comment_text": "", "digests": { "md5": "759d2146775052f88faf038073f19634", "sha256": "d8f7656f8e05145e164fa9b5269bc3f14fb17ecff5afc0b68a317dc22d56284c" }, "downloads": -1, "filename": "django-fsa-2.7.1.tar.gz", "has_sig": false, "md5_digest": "759d2146775052f88faf038073f19634", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13930, "upload_time": "2019-03-04T23:05:46", "upload_time_iso_8601": "2019-03-04T23:05:46.485096Z", "url": "https://files.pythonhosted.org/packages/bb/31/5c064a20ffbb83cde56c7a20312fa9529a2d67beec45fb20ca48fdab1312/django-fsa-2.7.1.tar.gz", "yanked": false, "yanked_reason": null } ], "2.7.2": [ { "comment_text": "", "digests": { "md5": "b01de2918dba518d3870e6b95f68ea55", "sha256": "94e221390d97b23b3e19ac5661b57f05c03c3f21121e5f8e3cf11f07bc483b4f" }, "downloads": -1, "filename": "django-fsa-2.7.2.tar.gz", "has_sig": false, "md5_digest": "b01de2918dba518d3870e6b95f68ea55", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13697, "upload_time": "2019-05-01T17:22:40", "upload_time_iso_8601": "2019-05-01T17:22:40.098538Z", "url": "https://files.pythonhosted.org/packages/36/61/455f16618bc6db4bbeed3ea36aad5809e280dbded569c425da22f2772342/django-fsa-2.7.2.tar.gz", "yanked": false, "yanked_reason": null } ], "2.8.0": [ { "comment_text": "", "digests": { "md5": "01871ab9f30a46bcc262433751f190d2", "sha256": "95470576398491bbac22f0ed1fd735b8d3fbd0e3151b741bb6b509cb3eaf6005" }, "downloads": -1, "filename": "django-fsa-2.8.0.tar.gz", "has_sig": false, "md5_digest": "01871ab9f30a46bcc262433751f190d2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13844, "upload_time": "2019-12-08T17:18:14", "upload_time_iso_8601": "2019-12-08T17:18:14.716556Z", "url": "https://files.pythonhosted.org/packages/65/79/948ea30e170af6f7bc3fcd1529878dc93e9c434debadab46c01b5d78b5dc/django-fsa-2.8.0.tar.gz", "yanked": false, "yanked_reason": null } ], "2.8.0.dev0": [ { "comment_text": "", "digests": { "md5": "8526c943d86fc06ac2ce4499fbf25988", "sha256": "0cadfaa71170e9fddea64db3e893a31268a68bbd4f056cd38d1b6c57f8825d6c" }, "downloads": -1, "filename": "django-fsa-2.8.0.dev0.tar.gz", "has_sig": false, "md5_digest": "8526c943d86fc06ac2ce4499fbf25988", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13855, "upload_time": "2019-10-29T21:01:02", "upload_time_iso_8601": "2019-10-29T21:01:02.552403Z", "url": "https://files.pythonhosted.org/packages/c1/70/cc93e3a3e1d45cace6c002630b2d2574ff5d1e1599d956ef542f0a11688c/django-fsa-2.8.0.dev0.tar.gz", "yanked": false, "yanked_reason": null } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "01871ab9f30a46bcc262433751f190d2", "sha256": "95470576398491bbac22f0ed1fd735b8d3fbd0e3151b741bb6b509cb3eaf6005" }, "downloads": -1, "filename": "django-fsa-2.8.0.tar.gz", "has_sig": false, "md5_digest": "01871ab9f30a46bcc262433751f190d2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13844, "upload_time": "2019-12-08T17:18:14", "upload_time_iso_8601": "2019-12-08T17:18:14.716556Z", "url": "https://files.pythonhosted.org/packages/65/79/948ea30e170af6f7bc3fcd1529878dc93e9c434debadab46c01b5d78b5dc/django-fsa-2.8.0.tar.gz", "yanked": false, "yanked_reason": null } ], "vulnerabilities": [] }