1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
Welcome to django-dirtyfields's documentation!
==============================================
Tracking dirty fields on a Django model instance.
Dirty means that field in-memory and database values are different.
`Source code <https://github.com/romgar/django-dirtyfields/>`_
Install
=======
::
$ pip install django-dirtyfields
Usage
=====
To use ``django-dirtyfields``, you need to:
- Inherit from ``DirtyFieldsMixin`` in the Django model you want to track.
::
from django.db import models
from dirtyfields import DirtyFieldsMixin
class TestModel(DirtyFieldsMixin, models.Model):
"""A simple test model to test dirty fields mixin with"""
boolean = models.BooleanField(default=True)
characters = models.CharField(blank=True, max_length=80)
- Use one of these 2 functions on a model instance to know if this instance is dirty, and get the dirty fields:
* is\_dirty()
* get\_dirty\_fields()
Examples
========
::
>>> from tests.models import TestModel
>>> tm = TestModel.objects.create(boolean=True,characters="testing")
>>> tm.is_dirty()
False
>>> tm.get_dirty_fields()
{}
>>> tm.boolean = False
>>> tm.is_dirty()
True
>>> tm.get_dirty_fields()
{'boolean': True}
Checking foreign key fields.
----------------------------
By default, dirty functions are not checking foreign keys. If you want to also take these relationships into account, use ``check_relationship`` parameter:
::
>>> from tests.models import TestModel
>>> tm = TestModel.objects.create(fkey=obj1)
>>> tm.is_dirty()
False
>>> tm.get_dirty_fields()
{}
>>> tm.fkey = obj2
>>> tm.is_dirty()
False
>>> tm.is_dirty(check_relationship=True)
True
>>> tm.get_dirty_fields()
{}
>>> tm.get_dirty_fields(check_relationship=True)
{'fkey': 1}
Checking many-to-many fields.
----------------------------
By default, dirty functions are not checking many-to-many fields. They are also a bit special, as a call to `.add()` method is directly
saving the related object to the database, thus the instance is never dirty.
If you want to check these relations, you should set ``ENABLE_M2M_CHECK`` to ``True`` in your model inheriting from
``DirtyFieldsMixin``, use ``check_m2m`` parameter and provide the values you want to test against:
::
class TestM2MModel(DirtyFieldsMixin, models.Model):
ENABLE_M2M_CHECK = True
m2m_field = models.ManyToManyField(AnotherModel)
>>> from tests.models import TestM2MModel
>>> tm = TestM2MModel.objects.create()
>>> tm2 = TestModel.objects.create()
>>> tm.is_dirty()
False
>>> tm.m2m_field.add(tm2)
>>> tm.is_dirty()
False
>>> tm.get_dirty_fields(check_m2m={'m2m_field': set([tm2.id])})
{}
>>> tm.get_dirty_fields(check_m2m={'m2m_field': set(["dummy_value])})
{'m2m_field': set([tm2.id])}
This can be useful when validating forms with m2m relations, where you receive some ids and want to know if your object
in the database needs to be updated with these form values.
**WARNING**: this m2m mode will generate extra queries to get m2m relation values each time you will save your objects.
It can have serious performance issues depending on your project.
Checking a limited set of model fields.
-------------------------------------
If you want to check a limited set of model fields, you should set ``FIELDS_TO_CHECK`` in your model inheriting from ``DirtyFieldsMixin``:
::
class TestModelWithSpecifiedFields(DirtyFieldsMixin, models.Model):
boolean1 = models.BooleanField(default=True)
boolean2 = models.BooleanField(default=True)
FIELDS_TO_CHECK = ['boolean1']
>>> from tests.models import TestModelWithSpecifiedFields
>>> tm = TestModelWithSpecifiedFields.objects.create()
>>> tm.boolean1 = False
>>> tm.boolean2 = False
>>> tm.get_dirty_fields()
{'boolean1': True}
This can be used in order to increase performance.
Saving dirty fields.
----------------------------
If you want to only save dirty fields from an instance in the database (only these fields will be involved in SQL query), you can use ``save_dirty_fields`` method.
Warning: this ``save_dirty_fields`` method will trigger the same signals as django default ``save`` method.
But, in django 1.4.22-, as we are using under the hood an ``update`` method, we need to manually send these signals, so be aware that only ``sender`` and ``instance`` arguments are passed to the signal in that context.
::
>>> tm.get_dirty_fields()
{'fkey': 1}
>>> tm.save_dirty_fields()
>>> tm.get_dirty_fields()
{}
Verbose mode
----------------------------
By default, when you use ``get_dirty_fields`` function, if there are dirty fields, only the old value is returned.
You can use ``verbose`` option to have more informations, for now a dict with old and new value:
::
>>> tm.get_dirty_fields()
{'fkey': 1}
>>> tm.get_dirty_fields(verbose=True)
{'fkey': {'saved': 1, 'current': 3}}
Custom comparison function
----------------------------
By default, ``dirtyfields`` compare the value between the database and the memory on a naive way (``==``).
After some issues (with timezones for example), a customisable comparison logic has been added.
You can now define how you want to compare 2 values by passing a function on DirtyFieldsMixin:
::
from django.db import models
from dirtyfields import DirtyFieldsMixin
def your_function((new_value, old_value, param1):
# Your custom comparison code here
return new_value == old_value
class TestModel(DirtyFieldsMixin, models.Model):
compare_function = (your_function, {'param1': 5})
Have a look at ``dirtyfields.compare`` module to get some examples.
Why would you want this?
========================
When using :mod:`django:django.db.models.signals` (:data:`django.db.models.signals.pre_save` especially), it is useful to be able to see what fields have changed or not. A signal could change its behaviour depending on whether a specific field has changed, whereas otherwise, you only could work on the event that the model's :meth:`~django.db.models.Model.save` method had been called.
.. include:: contributing.rst
.. include:: credits.rst
Table of Contents:
==================
.. toctree::
:maxdepth: 2
contributing
credits
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
|