django-tracking-model


Namedjango-tracking-model JSON
Version 0.1.7 PyPI version JSON
download
home_pagehttps://github.com/drozdowsky/django-tracking-model/
SummaryTrack changes made to django model instance fields.
upload_time2023-09-08 13:59:27
maintainer
docs_urlNone
authordrozdowsky
requires_python
licenseMIT
keywords
VCS
bugtrack_url
requirements Django flake8 pytest pytest-django tox psycopg2-binary
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Django Tracking Model / DTM 🏁
Track changes made to your model's instance fields.  
Changes are cleared on save.  
This package works well with [signals](https://seddonym.me/2018/05/04/django-signals/).  
Mutable fields (e.g. JSONField) are not handled with deepcopy to keep it fast and simple.  
Meant to be [model_utils](https://github.com/jazzband/django-model-utils)'s FieldTracker fast alternative.

*Available on [PyPi](https://pypi.org/project/django-tracking-model/)*  

## Installation
```sh
pip install django-tracking-model
```

## Usage
```python
from django.db import models
from tracking_model import TrackingModelMixin

# order matters
class Example(TrackingModelMixin, models.Model):
    text = models.TextField(null=True)
    myself = models.ForeignKey("self", null=True)
    array = models.ArrayField(TextField())
```
```python
In [1]: e = Example.objects.create(id=1, text="Sample Text")
In [2]: e.tracker.changed, e.tracker.newly_created
Out[1]: ({}, True)

In [3]: e.text = "Different Text"
In [4]: e.tracker.changed
Out[2]: {"text": "Sample Text"}

In [5]: e.save()
In [6]: e.tracker.changed, e.tracker.newly_created
Out[3]: ({}, False)
```
DTM will also detect changes made to ForeignKey/OneToOne fields.
```python
In [1]: Example.objects.create(myself=e)
In [2]: e.myself = None
In [3]: e.tracker.changed
Out[1]: {"myself_id": 1}
```
Because DTM does not handle mutable fields well, you handle them with copy/deepcopy.
```python
In [1]: e = Example.objects.create(array=["I", "am", "your"])
In [2]: copied = copy(e.array)
In [3]: copied.append("father")
In [4]: e.array = copied
In [5]: e.tracker.changed
Out[1]: {"array": ["I", "am", "your"]}

In [6]: e.array = ["Testing", "is", "the", "future"]  # in this case copy not needed
```
DTM works best with \*\_save signals.
```python
def pre_save_example(instance, *args, **kwargs):
    # .create() does not populate .changed, we use newly_created
    if "text" in instance.tracker.changed or instance.tracker.newly_created:
      if instance.text
          instance.array = instance.text.split()

pre_save.connect(pre_save_example, sender=Example)
```
```python
In [1]: e = Example.objects.create(text="I am your father")
In [2]: e.refresh_from_db() # not needed
In [3]: e.array
Out[1]: ["I", "am", "your", "father"]
```
DTM handles deferred fields well.
```python
In [1]: e = Example.objects.only("array").first()
In [2]: e.text = "I am not your father" 
In [3]: e.tracker.changed
Out[4]: {"text": DeferredAttribute}
```
You can narrow choice of tracked fields. By default everything is tracked.
```python
class Example(models.Model):
    TRACKED_FIELDS = ["first"]
    first = models.TextField()
    second = models.TextField()
```

## Requirements
 * Python >= 2.7, <= 3.11
 * Django >= 1.11, <= 4.x.y

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/drozdowsky/django-tracking-model/",
    "name": "django-tracking-model",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "",
    "author": "drozdowsky",
    "author_email": "hdrozdow+github@pm.me",
    "download_url": "",
    "platform": null,
    "description": "# Django Tracking Model / DTM \ud83c\udfc1\nTrack changes made to your model's instance fields.  \nChanges are cleared on save.  \nThis package works well with [signals](https://seddonym.me/2018/05/04/django-signals/).  \nMutable fields (e.g. JSONField) are not handled with deepcopy to keep it fast and simple.  \nMeant to be [model_utils](https://github.com/jazzband/django-model-utils)'s FieldTracker fast alternative.\n\n*Available on [PyPi](https://pypi.org/project/django-tracking-model/)*  \n\n## Installation\n```sh\npip install django-tracking-model\n```\n\n## Usage\n```python\nfrom django.db import models\nfrom tracking_model import TrackingModelMixin\n\n# order matters\nclass Example(TrackingModelMixin, models.Model):\n    text = models.TextField(null=True)\n    myself = models.ForeignKey(\"self\", null=True)\n    array = models.ArrayField(TextField())\n```\n```python\nIn [1]: e = Example.objects.create(id=1, text=\"Sample Text\")\nIn [2]: e.tracker.changed, e.tracker.newly_created\nOut[1]: ({}, True)\n\nIn [3]: e.text = \"Different Text\"\nIn [4]: e.tracker.changed\nOut[2]: {\"text\": \"Sample Text\"}\n\nIn [5]: e.save()\nIn [6]: e.tracker.changed, e.tracker.newly_created\nOut[3]: ({}, False)\n```\nDTM will also detect changes made to ForeignKey/OneToOne fields.\n```python\nIn [1]: Example.objects.create(myself=e)\nIn [2]: e.myself = None\nIn [3]: e.tracker.changed\nOut[1]: {\"myself_id\": 1}\n```\nBecause DTM does not handle mutable fields well, you handle them with copy/deepcopy.\n```python\nIn [1]: e = Example.objects.create(array=[\"I\", \"am\", \"your\"])\nIn [2]: copied = copy(e.array)\nIn [3]: copied.append(\"father\")\nIn [4]: e.array = copied\nIn [5]: e.tracker.changed\nOut[1]: {\"array\": [\"I\", \"am\", \"your\"]}\n\nIn [6]: e.array = [\"Testing\", \"is\", \"the\", \"future\"]  # in this case copy not needed\n```\nDTM works best with \\*\\_save signals.\n```python\ndef pre_save_example(instance, *args, **kwargs):\n    # .create() does not populate .changed, we use newly_created\n    if \"text\" in instance.tracker.changed or instance.tracker.newly_created:\n      if instance.text\n          instance.array = instance.text.split()\n\npre_save.connect(pre_save_example, sender=Example)\n```\n```python\nIn [1]: e = Example.objects.create(text=\"I am your father\")\nIn [2]: e.refresh_from_db() # not needed\nIn [3]: e.array\nOut[1]: [\"I\", \"am\", \"your\", \"father\"]\n```\nDTM handles deferred fields well.\n```python\nIn [1]: e = Example.objects.only(\"array\").first()\nIn [2]: e.text = \"I am not your father\" \nIn [3]: e.tracker.changed\nOut[4]: {\"text\": DeferredAttribute}\n```\nYou can narrow choice of tracked fields. By default everything is tracked.\n```python\nclass Example(models.Model):\n    TRACKED_FIELDS = [\"first\"]\n    first = models.TextField()\n    second = models.TextField()\n```\n\n## Requirements\n * Python >= 2.7, <= 3.11\n * Django >= 1.11, <= 4.x.y\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Track changes made to django model instance fields.",
    "version": "0.1.7",
    "project_urls": {
        "Homepage": "https://github.com/drozdowsky/django-tracking-model/"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d2dc90c2081ee1f8dd12995dde15dfadd23840b1a3f21e7be9af7e75ec6cf37f",
                "md5": "ea463c69217b0ae1a95ba12e5189fe53",
                "sha256": "719df534c450fbe841dd77e55f26fb69741723780add13ddb77961e90b64ec46"
            },
            "downloads": -1,
            "filename": "django_tracking_model-0.1.7-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "ea463c69217b0ae1a95ba12e5189fe53",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 4255,
            "upload_time": "2023-09-08T13:59:27",
            "upload_time_iso_8601": "2023-09-08T13:59:27.791336Z",
            "url": "https://files.pythonhosted.org/packages/d2/dc/90c2081ee1f8dd12995dde15dfadd23840b1a3f21e7be9af7e75ec6cf37f/django_tracking_model-0.1.7-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-09-08 13:59:27",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "drozdowsky",
    "github_project": "django-tracking-model",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [
        {
            "name": "Django",
            "specs": []
        },
        {
            "name": "flake8",
            "specs": []
        },
        {
            "name": "pytest",
            "specs": []
        },
        {
            "name": "pytest-django",
            "specs": []
        },
        {
            "name": "tox",
            "specs": []
        },
        {
            "name": "psycopg2-binary",
            "specs": []
        }
    ],
    "tox": true,
    "lcname": "django-tracking-model"
}
        
Elapsed time: 0.11757s