{ "info": { "author": "Grant McConnaughey", "author_email": "grantmcconnaughey@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Framework :: Django", "Framework :: Django :: 1.11", "Framework :: Django :: 2.0", "Framework :: Django :: 2.1", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Programming Language :: Python :: 2", "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" ], "description": "====================\ndjango-field-history\n====================\n\n.. image:: https://badge.fury.io/py/django-field-history.svg\n :target: https://badge.fury.io/py/django-field-history\n\n.. image:: https://readthedocs.org/projects/django-field-history/badge/?version=latest\n :target: https://django-field-history.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n.. image:: https://travis-ci.org/grantmcconnaughey/django-field-history.svg?branch=master\n :target: https://travis-ci.org/grantmcconnaughey/django-field-history\n\n.. image:: https://coveralls.io/repos/github/grantmcconnaughey/django-field-history/badge.svg?branch=master\n :target: https://coveralls.io/github/grantmcconnaughey/django-field-history?branch=master\n\nA Django app to track changes to a model field. For Python 2.7/3.4+ and Django 1.11/2.0+.\n\nOther similar apps are `django-reversion `_ and `django-simple-history `_, which track *all* model fields.\n\n+------------------------+----------------------+------------------+----------------------------------+\n| Project | django-field-history | django-reversion | django-simple-history |\n+------------------------+----------------------+------------------+----------------------------------+\n| Admin Integration | N/A | Yes | Yes |\n+------------------------+----------------------+------------------+----------------------------------+\n| All/Some fields | Some | Some | All |\n+------------------------+----------------------+------------------+----------------------------------+\n| Object History | No | Yes | Yes |\n+------------------------+----------------------+------------------+----------------------------------+\n| Model History | N/A | No | Yes |\n+------------------------+----------------------+------------------+----------------------------------+\n| Multi-object Revisions | N/A | Yes | No |\n+------------------------+----------------------+------------------+----------------------------------+\n| Extra Model Manager | Yes | No | Yes |\n+------------------------+----------------------+------------------+----------------------------------+\n| Model Registry | No | Yes | No |\n+------------------------+----------------------+------------------+----------------------------------+\n| Django View Helpers | No | Yes | No |\n+------------------------+----------------------+------------------+----------------------------------+\n| Manager Helper Methods | N/A | Yes | Yes (``as_of``, ``most_recent``) |\n+------------------------+----------------------+------------------+----------------------------------+\n| MySQL Support | Extra config | Complete | Complete |\n+------------------------+----------------------+------------------+----------------------------------+\n\nDocumentation\n-------------\n\nThe full documentation is at https://django-field-history.readthedocs.io.\n\nFeatures\n--------\n\n* Keeps a history of all changes to a particular model's field.\n* Stores the field's name, value, date and time of change, and the user that changed it.\n* Works with all model field types (except ``ManyToManyField``).\n\nQuickstart\n----------\n\nInstall django-field-history::\n\n pip install django-field-history\n\nBe sure to put it in INSTALLED_APPS.\n\n.. code-block:: python\n\n INSTALLED_APPS = [\n # other apps...\n 'field_history',\n ]\n\nThen add it to your models.\n\n.. code-block:: python\n\n from field_history.tracker import FieldHistoryTracker\n\n class PizzaOrder(models.Model):\n STATUS_CHOICES = (\n ('ORDERED', 'Ordered'),\n ('COOKING', 'Cooking'),\n ('COMPLETE', 'Complete'),\n )\n status = models.CharField(max_length=64, choices=STATUS_CHOICES)\n\n field_history = FieldHistoryTracker(['status'])\n\nNow each time you change the order's status field information about that change will be stored in the database.\n\n.. code-block:: python\n\n from field_history.models import FieldHistory\n\n # No FieldHistory objects yet\n assert FieldHistory.objects.count() == 0\n\n # Creating an object will make one\n pizza_order = PizzaOrder.objects.create(status='ORDERED')\n assert FieldHistory.objects.count() == 1\n\n # This object has some fields on it\n history = FieldHistory.objects.get()\n assert history.object == pizza_order\n assert history.field_name == 'status'\n assert history.field_value == 'ORDERED'\n assert history.date_created is not None\n\n # You can query FieldHistory using the get_{field_name}_history()\n # method added to your model\n histories = pizza_order.get_status_history()\n assert list(FieldHistory.objects.all()) == list(histories)\n\n # Or using the custom FieldHistory manager\n histories2 = FieldHistory.objects.get_for_model_and_field(pizza_order, 'status')\n assert list(histories) == list(histories2)\n\n # Updating that particular field creates a new FieldHistory\n pizza_order.status = 'COOKING'\n pizza_order.save()\n assert FieldHistory.objects.count() == 2\n\n updated_history = histories.latest()\n assert updated_history.object == pizza_order\n assert updated_history.field_name == 'status'\n assert updated_history.field_value == 'COOKING'\n assert updated_history.date_created is not None\n\nManagement Commands\n-------------------\n\ndjango-field-history comes with a few management commands.\n\ncreateinitialfieldhistory\n+++++++++++++++++++++++++\n\nThis command will inspect all of the models in your application and create ``FieldHistory`` objects for the models that have a ``FieldHistoryTracker``. Run this the first time you install django-field-history.\n\n::\n\n python manage.py createinitialfieldhistory\n\nrenamefieldhistory\n++++++++++++++++++\n\nUse this command after changing a model field name of a field you track with ``FieldHistoryTracker``::\n\n python manage.py renamefieldhistory --model=app_label.model_name --from_field=old_field_name --to_field=new_field_name\n\nFor instance, if you have this model:\n\n.. code-block:: python\n\n class Person(models.Model):\n username = models.CharField(max_length=255)\n\n field_history = FieldHistoryTracker(['username'])\n\nAnd you change the ``username`` field name to ``handle``:\n\n.. code-block:: python\n\n class Person(models.Model):\n handle = models.CharField(max_length=255)\n\n field_history = FieldHistoryTracker(['handle'])\n\nYou will need to also update the ``field_name`` value in all ``FieldHistory`` objects that point to this model::\n\n python manage.py renamefieldhistory --model=myapp.Person --from_field=username --to_field=handle\n\nStoring Which User Changed the Field\n------------------------------------\n\nThere are two ways to store the user that changed your model field. The simplest way is to use **the logged in user** that made the request. To do this, add the ``FieldHistoryMiddleware`` class to your ``MIDDLEWARE`` setting.\n\n.. code-block:: python\n\n MIDDLEWARE = [\n 'django.contrib.sessions.middleware.SessionMiddleware',\n 'django.middleware.common.CommonMiddleware',\n 'django.contrib.auth.middleware.AuthenticationMiddleware',\n 'field_history.middleware.FieldHistoryMiddleware',\n ]\n\nAlternatively, you can add a ``_field_history_user`` property to the model that has fields you are tracking. This property should return the user you would like stored on ``FieldHistory`` when your field is updated.\n\n.. code-block:: python\n\n class Pizza(models.Model):\n name = models.CharField(max_length=255)\n updated_by = models.ForeignKey('auth.User')\n\n field_history = FieldHistoryTracker(['name'])\n\n @property\n def _field_history_user(self):\n return self.updated_by\n\nWorking with MySQL\n------------------\n\nIf you're using MySQL, the default configuration will throw an exception when you run migrations. (By default, ``FieldHistory.object_id`` is implemented as a ``TextField`` for flexibility, but indexed columns in MySQL InnoDB tables may be a maximum of 767 bytes.) To fix this, you can set ``FIELD_HISTORY_OBJECT_ID_TYPE`` in settings.py to override the default field type with one that meets MySQL's constraints. ``FIELD_HISTORY_OBJECT_ID_TYPE`` may be set to either:\n\n1. the Django model field class you wish to use, or\n2. a tuple ``(field_class, kwargs)``, where ``field_class`` is a Django model field class and ``kwargs`` is a dict of arguments to pass to the field class constructor.\n\nTo approximate the default behavior for Postgres when using MySQL, configure ``object_id`` to use a ``CharField`` by adding the following to settings.py:\n\n.. code-block:: python\n\n from django.db import models\n FIELD_HISTORY_OBJECT_ID_TYPE = (models.CharField, {'max_length': 100})\n\n``FIELD_HISTORY_OBJECT_ID_TYPE`` also allows you to use a field type that's more efficient for your use case, even if you're using Postgres (or a similarly unconstrained database). For example, if you always let Django auto-create an ``id`` field (implemented internally as an ``AutoField``), setting ``FIELD_HISTORY_OBJECT_ID_TYPE`` to ``IntegerField`` will result in efficiency gains (both in time and space). This would look like:\n\n.. code-block:: python\n\n from django.db import models\n FIELD_HISTORY_OBJECT_ID_TYPE = models.IntegerField\n\nRunning Tests\n-------------\n\nDoes the code actually work?\n\n::\n\n source /bin/activate\n (myenv) $ pip install -r requirements-test.txt\n (myenv) $ python runtests.py\n\n\n\n\nHistory\n-------\n\n0.7.0 (September 3, 2018)\n++++++++++++++++++++\n* Added support for Django 2.0 and 2.1\n* Added support for Python 3.7\n* Dropped support for Django 1.7 through 1.10\n* Dropped support for Python 3.2 and 3.3\n* Fixed generic primary key bug with `createinitialfieldhistory` command (#20)\n\n0.6.0 (December 22, 2016)\n+++++++++++++++++++++++++\n* Added Django 1.10 compatibility.\n* Added MySQL compatibility.\n* Fixed issue that would duplicate tracked fields.\n\n0.5.0 (April 16, 2016)\n++++++++++++++++++++++\n* Added the ability to track field history of parent models.\n* Added Django 1.7 compatibility.\n\n0.4.0 (February 24, 2016)\n+++++++++++++++++++++++++\n* Added a way to automatically store the logged in user on ``FieldHistory.user``.\n\n0.3.0 (February 20, 2016)\n+++++++++++++++++++++++++\n\n* ``FieldHistory`` objects are now created using ``bulk_create``, which means only one query will be executed, even when changing multiple fields at the same time.\n* Added a way to store which user updated a field.\n* Added ``get_latest_by`` to ``FieldHistory`` Meta options so ``.latest()`` and ``.earliest()`` can be used.\n* Added ``createinitialfieldhistory`` management command.\n* Added ``renamefieldhistory`` management command.\n\n0.2.0 (February 17, 2016)\n+++++++++++++++++++++++++\n\n* First release on PyPI.\n\n\n", "description_content_type": "", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/grantmcconnaughey/django-field-history", "keywords": "django-field-history", "license": "BSD", "maintainer": "", "maintainer_email": "", "name": "django-field-history", "package_url": "https://pypi.org/project/django-field-history/", "platform": "", "project_url": "https://pypi.org/project/django-field-history/", "project_urls": { "Homepage": "https://github.com/grantmcconnaughey/django-field-history" }, "release_url": "https://pypi.org/project/django-field-history/0.7.0/", "requires_dist": null, "requires_python": "", "summary": "A Django app to track changes to a model field.", "version": "0.7.0" }, "last_serial": 4235026, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "ec3b17058f7b359812efdab682786f93", "sha256": "f746b6da65e14f38ecc644be41ad5890984e2e500923150fb91a159476b18311" }, "downloads": -1, "filename": "django-field-history-0.1.0.tar.gz", "has_sig": false, "md5_digest": "ec3b17058f7b359812efdab682786f93", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8162, "upload_time": "2016-02-17T20:28:24", "url": "https://files.pythonhosted.org/packages/a7/9f/ddd6f3749714afa6d0fc1b82681f39c1b08f4d7666477ec95bba0d988190/django-field-history-0.1.0.tar.gz" } ], "0.2.0": [ { "comment_text": "", "digests": { "md5": "c389dfec53afca886a5c93580873ac9d", "sha256": "7c4392d1e55a54cb2a2f894e717704f7e530bf7a7de765272e5096fbc0dfb443" }, "downloads": -1, "filename": "django_field_history-0.2.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c389dfec53afca886a5c93580873ac9d", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 8817, "upload_time": "2016-02-17T20:30:23", "url": "https://files.pythonhosted.org/packages/96/2a/f7873f7168830a9bfdcad91191dbb49c128731412a3efc6073930479fb7d/django_field_history-0.2.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "35f86f05d51ab4c94a7887e3b1c279d9", "sha256": "46f5683a18e8bb7513dfed5ef7c5cddad1d3cef376096e72f63ae3cc7664dc8b" }, "downloads": -1, "filename": "django-field-history-0.2.0.tar.gz", "has_sig": false, "md5_digest": "35f86f05d51ab4c94a7887e3b1c279d9", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 8168, "upload_time": "2016-02-17T20:30:16", "url": "https://files.pythonhosted.org/packages/70/57/959e233e87da3025e2cead6dc8a3ca342dc2b8bbdac21c8afeaeb2ef0666/django-field-history-0.2.0.tar.gz" } ], "0.3.0": [ { "comment_text": "", "digests": { "md5": "4b2a2339d80fe6cd70b10354cbff6c35", "sha256": "ae4bc813d2b5f3ac117a8673074f6bb9bfd68dbdf5f3f82ec4f001c8394ea208" }, "downloads": -1, "filename": "django_field_history-0.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "4b2a2339d80fe6cd70b10354cbff6c35", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 11960, "upload_time": "2016-02-20T18:06:19", "url": "https://files.pythonhosted.org/packages/ac/de/9890ea3ac7609b40922fb3cc54dff7ee4862b9f08a1e18a36040c163bc6c/django_field_history-0.3.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "ad2e48efd75118d29eff8981a684da2b", "sha256": "27508865ec03128ff581260ac7549409cb5dc794b3d37e59293e7b9d1a90248d" }, "downloads": -1, "filename": "django-field-history-0.3.0.tar.gz", "has_sig": false, "md5_digest": "ad2e48efd75118d29eff8981a684da2b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11452, "upload_time": "2016-02-20T18:06:10", "url": "https://files.pythonhosted.org/packages/39/a6/3881103e43e5c5d334486eec3b2113444c1bfbc40919a2cc2c6ada6f522f/django-field-history-0.3.0.tar.gz" } ], "0.4.0": [ { "comment_text": "", "digests": { "md5": "e0ad4e30054b8678608cac191b7e0492", "sha256": "7a8998ce6654041e9fd5979959e2f1bd5b5a7a52f6e0f2eeaf5cef7bc59620d0" }, "downloads": -1, "filename": "django_field_history-0.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "e0ad4e30054b8678608cac191b7e0492", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 12819, "upload_time": "2016-02-24T22:49:40", "url": "https://files.pythonhosted.org/packages/fc/4f/25b70f44e29c217b1ec7faf92a6b193538b680c0e13bd60117fc23cf3840/django_field_history-0.4.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "da69e124f3fab53d6528423d2e9d5d45", "sha256": "d5dd5f6d7a8e594e83d3589d9e8623acb99621acd5261a34f1e8e15508d1be8e" }, "downloads": -1, "filename": "django-field-history-0.4.0.tar.gz", "has_sig": false, "md5_digest": "da69e124f3fab53d6528423d2e9d5d45", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 12085, "upload_time": "2016-02-24T22:49:29", "url": "https://files.pythonhosted.org/packages/ca/a9/289179f7dcad04f3fc0a490ef85d59be93e82808fb864814649b36e3fc7c/django-field-history-0.4.0.tar.gz" } ], "0.5.0": [ { "comment_text": "", "digests": { "md5": "c46a45440de7c6fb4c89b957795dc5d5", "sha256": "86a404949f4e4b0af1bd880dfe2f21b444e75cbf79591efc57e920e503b4781e" }, "downloads": -1, "filename": "django_field_history-0.5.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c46a45440de7c6fb4c89b957795dc5d5", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 14580, "upload_time": "2016-04-16T20:27:02", "url": "https://files.pythonhosted.org/packages/83/0a/10e35f028694ca1d3d0e4e44bbee1f9df098cb548251d8ae553890ba86f7/django_field_history-0.5.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "db2908efee8e1838728dd23692194497", "sha256": "4c200180b0c9e8444a36265ec2a3eb967bdb060e1c8db4670f97fae5554f9cfd" }, "downloads": -1, "filename": "django-field-history-0.5.0.tar.gz", "has_sig": false, "md5_digest": "db2908efee8e1838728dd23692194497", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 13090, "upload_time": "2016-04-16T20:26:52", "url": "https://files.pythonhosted.org/packages/6d/06/e1a7e337b53e944a8c8de4e0019eb0f8b321cf9b0bc370049532bce2f461/django-field-history-0.5.0.tar.gz" } ], "0.6.0": [ { "comment_text": "", "digests": { "md5": "c76d1b2186008a94a243d928f15f7d8f", "sha256": "f942a038f5316e309e19d0b89f49815a19aa9161694775a50f9de8949df63571" }, "downloads": -1, "filename": "django_field_history-0.6.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c76d1b2186008a94a243d928f15f7d8f", "packagetype": "bdist_wheel", "python_version": "2.7", "requires_python": null, "size": 16526, "upload_time": "2016-12-23T00:09:00", "url": "https://files.pythonhosted.org/packages/14/84/1f1ae69828ef5cfbe3abe748491036bce37a93068e82d53eb5133f02935a/django_field_history-0.6.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "a4f3eabf1419e3981c24584ca545c2d7", "sha256": "e5203df7817da9c4e380f96fcbb56fa652439ec184ec1a240a228224b53c673d" }, "downloads": -1, "filename": "django-field-history-0.6.0.tar.gz", "has_sig": false, "md5_digest": "a4f3eabf1419e3981c24584ca545c2d7", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 15220, "upload_time": "2016-12-23T00:08:58", "url": "https://files.pythonhosted.org/packages/7c/17/82e2bb85306ee16e62091dbe71825f2c1909e0e107159021c224e9dba555/django-field-history-0.6.0.tar.gz" } ], "0.7.0": [ { "comment_text": "", "digests": { "md5": "90502f2d31b2d5923bc8ce924ba268fa", "sha256": "bbda449896da166698c48a1b05cebbd61dd548e516b1c45d09d364c5e3dee25b" }, "downloads": -1, "filename": "django_field_history-0.7.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "90502f2d31b2d5923bc8ce924ba268fa", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13052, "upload_time": "2018-09-03T17:45:00", "url": "https://files.pythonhosted.org/packages/1f/d0/e50d14c75d811cad6338c314c9f849e4571d7d32e11945e6ac296f1c34ce/django_field_history-0.7.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3a91a0955538cc4e610785abbea35314", "sha256": "ffdccf5b123533062f5cc6293c10a9eaf683b5844af01acd1b704242e9efcb26" }, "downloads": -1, "filename": "django-field-history-0.7.0.tar.gz", "has_sig": false, "md5_digest": "3a91a0955538cc4e610785abbea35314", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16105, "upload_time": "2018-09-03T17:45:02", "url": "https://files.pythonhosted.org/packages/6c/17/e6f31b96e4f247a6c10573a4e80fb4a84ed6aa65fbc52a6e2a7d358a22fa/django-field-history-0.7.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "90502f2d31b2d5923bc8ce924ba268fa", "sha256": "bbda449896da166698c48a1b05cebbd61dd548e516b1c45d09d364c5e3dee25b" }, "downloads": -1, "filename": "django_field_history-0.7.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "90502f2d31b2d5923bc8ce924ba268fa", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 13052, "upload_time": "2018-09-03T17:45:00", "url": "https://files.pythonhosted.org/packages/1f/d0/e50d14c75d811cad6338c314c9f849e4571d7d32e11945e6ac296f1c34ce/django_field_history-0.7.0-py2.py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "3a91a0955538cc4e610785abbea35314", "sha256": "ffdccf5b123533062f5cc6293c10a9eaf683b5844af01acd1b704242e9efcb26" }, "downloads": -1, "filename": "django-field-history-0.7.0.tar.gz", "has_sig": false, "md5_digest": "3a91a0955538cc4e610785abbea35314", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16105, "upload_time": "2018-09-03T17:45:02", "url": "https://files.pythonhosted.org/packages/6c/17/e6f31b96e4f247a6c10573a4e80fb4a84ed6aa65fbc52a6e2a7d358a22fa/django-field-history-0.7.0.tar.gz" } ] }