{ "info": { "author": "Robert Singer", "author_email": "robertgsinger@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7" ], "description": "# Django Lifecycle Hooks\n\n[![Package version](https://badge.fury.io/py/django-lifecycle.svg)](https://pypi.python.org/pypi/django-lifecycle)\n[![Python versions](https://img.shields.io/pypi/status/django-lifecycle.svg)](https://img.shields.io/pypi/status/django-lifecycle.svg/)\n\nThis project provides a `@hook` decorator as well as a base model and mixin to add lifecycle hooks to your Django models. Django's built-in approach to offering lifecycle hooks is [Signals](https://docs.djangoproject.com/en/dev/topics/signals/). However, my team often finds that Signals introduce unnesseary indirection and are at odds with Django's \"fat models\" approach.\n\nIn short, you can write model code like this:\n\n```python\nfrom django_lifecycle import LifecycleModel, hook\n\n\nclass Article(LifecycleModel):\n contents = models.TextField()\n updated_at = models.DateTimeField(null=True)\n status = models.ChoiceField(choices=['draft', 'published'])\n editor = models.ForeignKey(AuthUser)\n\n @hook('before_update', when='contents', has_changed=True)\n def on_content_change(self):\n self.updated_at = timezone.now()\n\n @hook('after_update', when=\"status\", was=\"draft\", is_now=\"published\")\n def on_publish(self):\n send_email(self.editor.email, \"An article has published!\")\n```\n\nInstead of overriding `save` and `__init___` in a clunky way that hurts readability:\n\n```python\n # same class and field declarations as above ...\n \n def __init__(self, *args, **kwargs):\n super().__init__(*args, **kwargs)\n self._orig_contents = self.contents\n self._orig_status = self.status\n \n \n def save(self, *args, **kwargs):\n if self.pk is not None and self.contents != self._orig_contents):\n self.updated_at = timezone.now()\n\n super().save(*args, **kwargs)\n\n if self.status != self._orig_status:\n send_email(self.editor.email, \"An article has published!\")\n```\n\n---\n\n**Documentation**: https://rsinger86.github.io/django-lifecycle\n\n**Source Code**: https://github.com/rsinger86/django-lifecycle\n\n---\n\n# Changelog\n\n## 0.6.0 (October 2019)\n* Adds `when_any` hook paramter to watch multiple fields for state changes\n\n## 0.5.0 (September 2019)\n* Adds `was_not` condition\n* Allow watching changes to FK model field values, not just FK references\n\n## 0.4.2 (July 2019)\n* Fixes missing README.md issue that broke install.\n\n## 0.4.1 (June 2019)\n* Fixes [urlman](https://github.com/andrewgodwin/urlman)-compatability.\n\n## 0.4.0 (May 2019)\n* Fixes `initial_value(field_name)` behavior - should return value even if no change. Thanks @adamJLev!\n\n## 0.3.2 (February 2019)\n* Fixes bug preventing hooks from firing for custom PKs. Thanks @atugushev!\n\n## 0.3.1 (August 2018)\n* Fixes m2m field bug, in which accessing auto-generated reverse field in `before_create` causes exception b/c PK does not exist yet. Thanks @garyd203!\n\n## 0.3.0 (April 2018)\n* Resets model's comparison state for hook conditions after `save` called.\n\n## 0.2.4 (April 2018)\n* Fixed support for adding multiple `@hook` decorators to same method.\n\n## 0.2.3 (April 2018)\n* Removes residual mixin methods from earlier implementation.\n\n## 0.2.2 (April 2018)\n* Save method now accepts `skip_hooks`, an optional boolean keyword argument that controls whether hooked methods are called.\n\n## 0.2.1 (April 2018)\n* Fixed bug in `_potentially_hooked_methods` that caused unwanted side effects by accessing model instance methods decorated with `@cache_property` or `@property`. \n\n## 0.2.0 (April 2018)\n* Added Django 1.8 support. Thanks @jtiai!\n* Tox testing added for Python 3.4, 3.5, 3.6 and Django 1.8, 1.11 and 2.0. Thanks @jtiai!\n\n# Testing\n\nTests are found in a simplified Django project in the ```/tests``` folder. Install the project requirements and do ```./manage.py test``` to run them.\n\n# License\n\nSee [License](LICENSE.md).", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/rsinger86/django-lifecycle", "keywords": "django model lifecycle hooks callbacks", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "django-lifecycle", "package_url": "https://pypi.org/project/django-lifecycle/", "platform": "", "project_url": "https://pypi.org/project/django-lifecycle/", "project_urls": { "Homepage": "https://github.com/rsinger86/django-lifecycle" }, "release_url": "https://pypi.org/project/django-lifecycle/0.6.0/", "requires_dist": null, "requires_python": "", "summary": "Declarative model lifecycle hooks.", "version": "0.6.0" }, "last_serial": 5942955, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "afc2a507fd7361de33eccfd990bfe277", "sha256": "595241e758187a74c4726c2086791f397f82cf0c59b35851583c115a655a3215" }, "downloads": -1, "filename": "django-lifecycle-0.1.0.tar.gz", "has_sig": false, "md5_digest": "afc2a507fd7361de33eccfd990bfe277", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6275, "upload_time": "2018-03-28T13:16:19", "url": "https://files.pythonhosted.org/packages/04/a4/9b5004160ee8d5e928827271ed7007a7c01119f27c99a29e79de328c65f9/django-lifecycle-0.1.0.tar.gz" } ], "0.1.1": [ { "comment_text": "", "digests": { "md5": "e26e2c6773000fb05d663e7d0757d1cc", "sha256": "e6d58224ee6c3db4c266096332669518ac35f0a20284ab7386aa53c9d37a960d" }, "downloads": -1, "filename": "django-lifecycle-0.1.1.tar.gz", "has_sig": false, "md5_digest": "e26e2c6773000fb05d663e7d0757d1cc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6364, "upload_time": "2018-03-30T00:06:00", "url": "https://files.pythonhosted.org/packages/57/2e/d04b2347a4de14a531988f205fe7ca7ff5c37ab9d569be92b8408c854a14/django-lifecycle-0.1.1.tar.gz" } ], "0.1.2": [ { "comment_text": "", "digests": { "md5": "2129d086f422ae9c5fd0287c3bd99dad", "sha256": "24cc8481e8ddfdc760f2ba7ebfb0b2423e3e051f5a9bfd5552b1468fede9aded" }, "downloads": -1, "filename": "django-lifecycle-0.1.2.tar.gz", "has_sig": false, "md5_digest": "2129d086f422ae9c5fd0287c3bd99dad", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6460, "upload_time": "2018-03-31T18:28:44", "url": "https://files.pythonhosted.org/packages/38/f3/46f2f611628f8855ad3207e0b59a3be5d0a62a9aeb5bbd42b9e70ea138fe/django-lifecycle-0.1.2.tar.gz" } ], "0.1.3": [ { "comment_text": "", "digests": { "md5": "2edb3618f87f8f6ad736d80498847f88", "sha256": "22da4a5b75a524c5ae7b56c3edc17917e2699590694ff3060d483054091fe6d1" }, "downloads": -1, "filename": "django-lifecycle-0.1.3.tar.gz", "has_sig": false, "md5_digest": "2edb3618f87f8f6ad736d80498847f88", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6420, "upload_time": "2018-03-31T18:35:25", "url": "https://files.pythonhosted.org/packages/4b/89/d6e0163db328aff97ba2a4eb99a41304485d1bfad1184e04e50d1a87f170/django-lifecycle-0.1.3.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "c28e543103b859092332213735a44bb0", "sha256": "40bdd64dfb68e7f58c0cbb599bb27bf4cd428194ee92e9196b1952472aa5a055" }, "downloads": -1, "filename": "django-lifecycle-0.2.0.tar.gz", "has_sig": false, "md5_digest": "c28e543103b859092332213735a44bb0", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 6916, "upload_time": "2018-04-04T01:32:38", "url": "https://files.pythonhosted.org/packages/2f/25/25f143ae3286feda292131f353ea41f88c9f74eeeacef951747162d293d7/django-lifecycle-0.2.0.tar.gz" } ], "0.2.1": [ { "comment_text": "", "digests": { "md5": "ac754c0d346d79346c241e9f5efa71f6", "sha256": "d4827c032305c6ad23d7f3c35d482d3654500a3bebaf745775f4a3ce7c47f0b5" }, "downloads": -1, "filename": "django-lifecycle-0.2.1.tar.gz", "has_sig": false, "md5_digest": "ac754c0d346d79346c241e9f5efa71f6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7433, "upload_time": "2018-04-07T22:05:34", "url": "https://files.pythonhosted.org/packages/e7/63/7862d9f9d47b5c7b579494bab7f499b4563712e6ea6169dc003dc84d3c71/django-lifecycle-0.2.1.tar.gz" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "6c103caafe9f4f324f5c8e32e20c33d7", "sha256": "21c7333379130dcf5f7075732741a5a0cdcad13f30b29dcc31d5dfce172303a0" }, "downloads": -1, "filename": "django-lifecycle-0.2.2.tar.gz", "has_sig": false, "md5_digest": "6c103caafe9f4f324f5c8e32e20c33d7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7626, "upload_time": "2018-04-07T22:49:39", "url": "https://files.pythonhosted.org/packages/a4/76/0cd558aad01eff7acb6eeff2ffad9bab58b6e15a60aeb943d9a2240cf260/django-lifecycle-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "7057dbac418d8f02b0aeba4b00027817", "sha256": "1eed1d5e4f2472aaa02811fabc8df1131fba8cdda4e47901f0461e53a686f961" }, "downloads": -1, "filename": "django-lifecycle-0.2.3.tar.gz", "has_sig": false, "md5_digest": "7057dbac418d8f02b0aeba4b00027817", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7611, "upload_time": "2018-04-08T15:45:44", "url": "https://files.pythonhosted.org/packages/31/e1/e17b810281885203e48f5be640689508504286cc6193647bca40d9c8a897/django-lifecycle-0.2.3.tar.gz" } ], "0.2.4": [ { "comment_text": "", "digests": { "md5": "876575c142af5de3718ff878eee9986c", "sha256": "0fcaa93d23a0344aa33ecd2384d7023b4ab1b201e0f6e910501397c294440b14" }, "downloads": -1, "filename": "django-lifecycle-0.2.4.tar.gz", "has_sig": false, "md5_digest": "876575c142af5de3718ff878eee9986c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7753, "upload_time": "2018-04-10T02:52:03", "url": "https://files.pythonhosted.org/packages/92/ff/4b888f49a9b7845db05db960e7110badffb58d485d614ad2e3cb56442d2d/django-lifecycle-0.2.4.tar.gz" } ], "0.2.5": [ { "comment_text": "", "digests": { "md5": "3c55bebee61c1c1458de85fec5bdd2f5", "sha256": "93537c963bec3c60f566d2e48aa98ab41fedb939c86d3b6f8b411f569c909079" }, "downloads": -1, "filename": "django-lifecycle-0.2.5.tar.gz", "has_sig": false, "md5_digest": "3c55bebee61c1c1458de85fec5bdd2f5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7897, "upload_time": "2018-04-10T03:17:44", "url": "https://files.pythonhosted.org/packages/ab/21/6ab9643effae2e6bfd38f059d5402d1bb63a568d875dd434b5870db96313/django-lifecycle-0.2.5.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "6d947a9887848d88279f58f570a44991", "sha256": "6372f6850951e1ccaddb50b741020ff5ff2230b50e18e25fded5bf2dd9cdef2e" }, "downloads": -1, "filename": "django-lifecycle-0.3.0.tar.gz", "has_sig": false, "md5_digest": "6d947a9887848d88279f58f570a44991", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7975, "upload_time": "2018-04-13T01:14:18", "url": "https://files.pythonhosted.org/packages/e7/55/94934ff9e333a63942e8803768757e5cb8dbb5a095dcfbeed5d550960263/django-lifecycle-0.3.0.tar.gz" } ], "0.3.1": [ { "comment_text": "", "digests": { "md5": "9ee119de2fd2244292cbedab648c45bc", "sha256": "90940b7eae5b526deb0ad76d18d5da13555460fc77f1c9f6129b6948fcdee429" }, "downloads": -1, "filename": "django-lifecycle-0.3.1.tar.gz", "has_sig": false, "md5_digest": "9ee119de2fd2244292cbedab648c45bc", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8598, "upload_time": "2018-08-19T05:23:56", "url": "https://files.pythonhosted.org/packages/8e/84/bd12db7b8557c3c54be78ca7a3280f278e7726968b181336d01a601d1e7d/django-lifecycle-0.3.1.tar.gz" } ], "0.3.2": [ { "comment_text": "", "digests": { "md5": "97ee7f482174cd1780cda0fb4842e2a1", "sha256": "19f213d962e73fa1e2fdde04964f59bd49648606cc5e596ef47ce376e04ad326" }, "downloads": -1, "filename": "django-lifecycle-0.3.2.tar.gz", "has_sig": false, "md5_digest": "97ee7f482174cd1780cda0fb4842e2a1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8711, "upload_time": "2019-03-07T02:27:24", "url": "https://files.pythonhosted.org/packages/6a/be/62015fa21b21f248ecfbe895627fe3cd8102b2e0a765912e121a9a1663bd/django-lifecycle-0.3.2.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "76c953c30be4912466dd467554e0a5ce", "sha256": "a4b02f6b997f74e7ad2bba4e24dcd5bf40ed2bc7e0e2816954c9fa93bbfa161d" }, "downloads": -1, "filename": "django-lifecycle-0.4.0.tar.gz", "has_sig": false, "md5_digest": "76c953c30be4912466dd467554e0a5ce", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8818, "upload_time": "2019-05-11T20:52:30", "url": "https://files.pythonhosted.org/packages/26/ee/3567041e69ceefcf68ab92da2c31108759409303c80a7f99922a5a203e3d/django-lifecycle-0.4.0.tar.gz" } ], "0.4.1": [ { "comment_text": "", "digests": { "md5": "cfbb25ed9f727b73a2d152f906c0a655", "sha256": "a49a7ded1ab46f25135a67f5b6886e85571c129c058e0ac3642680c9647fb3a0" }, "downloads": -1, "filename": "django-lifecycle-0.4.1.tar.gz", "has_sig": false, "md5_digest": "cfbb25ed9f727b73a2d152f906c0a655", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 9507, "upload_time": "2019-06-30T21:37:19", "url": "https://files.pythonhosted.org/packages/d4/ab/9daddd333fdf41bf24da744818a00ce8caa8e39d93da466b752b291ce412/django-lifecycle-0.4.1.tar.gz" } ], "0.4.2": [ { "comment_text": "", "digests": { "md5": "eb003632d20bfa176fbcd3fb87d3e5ff", "sha256": "4517255d87c1e5b2394d502aeafe7acc12cf286c49807f701ce4cced8c8f865a" }, "downloads": -1, "filename": "django-lifecycle-0.4.2.tar.gz", "has_sig": false, "md5_digest": "eb003632d20bfa176fbcd3fb87d3e5ff", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8979, "upload_time": "2019-07-06T18:13:02", "url": "https://files.pythonhosted.org/packages/b2/51/3bf43cccce7aadf663fad7e68bf999a7b0417e74b2344647f925e1b2e1c1/django-lifecycle-0.4.2.tar.gz" } ], "0.5.0": [ { "comment_text": "", "digests": { "md5": "c089cef1741282842d68f80e0325628a", "sha256": "60ee9aad8a1fa3ab2410b60cfd93f4299e928c57f9b8c8c90a49a37aaa0d744d" }, "downloads": -1, "filename": "django-lifecycle-0.5.0.tar.gz", "has_sig": false, "md5_digest": "c089cef1741282842d68f80e0325628a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10690, "upload_time": "2019-09-29T18:51:53", "url": "https://files.pythonhosted.org/packages/e7/34/a559e8fc04883f9523afb08ac214b345c73983d6f4df082458936635011f/django-lifecycle-0.5.0.tar.gz" } ], "0.6.0": [ { "comment_text": "", "digests": { "md5": "c0618f24712f0ca3fd168c595ba3635b", "sha256": "de5000e2ccd1435ddd6872f12a9c237b0ea4077ac1f85dd7cb2bd2cf5f658b15" }, "downloads": -1, "filename": "django-lifecycle-0.6.0.tar.gz", "has_sig": false, "md5_digest": "c0618f24712f0ca3fd168c595ba3635b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7180, "upload_time": "2019-10-08T05:34:31", "url": "https://files.pythonhosted.org/packages/9b/cb/9c7ea329c8735a0acb8ab73186714efd542fc41e1eac6a03cdf289357088/django-lifecycle-0.6.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "c0618f24712f0ca3fd168c595ba3635b", "sha256": "de5000e2ccd1435ddd6872f12a9c237b0ea4077ac1f85dd7cb2bd2cf5f658b15" }, "downloads": -1, "filename": "django-lifecycle-0.6.0.tar.gz", "has_sig": false, "md5_digest": "c0618f24712f0ca3fd168c595ba3635b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 7180, "upload_time": "2019-10-08T05:34:31", "url": "https://files.pythonhosted.org/packages/9b/cb/9c7ea329c8735a0acb8ab73186714efd542fc41e1eac6a03cdf289357088/django-lifecycle-0.6.0.tar.gz" } ] }