{"id":5187,"library":"django-dirtyfields","title":"Django Dirty Fields","description":"django-dirtyfields is a small library for tracking 'dirty' fields on a Django model instance. A field is considered dirty if its in-memory value differs from the value currently saved in the database. The library is currently at version 1.9.9 and is actively maintained with regular updates to support new Django and Python versions.","status":"active","version":"1.9.9","language":"en","source_language":"en","source_url":"https://github.com/romgar/django-dirtyfields.git","tags":["django","model","fields","tracking","dirty","orm"],"install":[{"cmd":"pip install django-dirtyfields","lang":"bash","label":"Install with pip"}],"dependencies":[{"reason":"Core framework dependency for model integration. Requires Django >=3.2.","package":"Django"}],"imports":[{"symbol":"DirtyFieldsMixin","correct":"from dirtyfields import DirtyFieldsMixin"}],"quickstart":{"code":"from django.db import models\nfrom dirtyfields import DirtyFieldsMixin\n\nclass ExampleModel(DirtyFieldsMixin, models.Model):\n    characters = models.CharField(max_length=80, default='initial value')\n    boolean = models.BooleanField(default=True)\n\n    def __str__(self):\n        return self.characters\n\n# Example Usage:\n# Assuming an active Django environment and database\n# model = ExampleModel.objects.create(characters='first value', boolean=True)\n# print(f'Is dirty initially? {model.is_dirty()}') # Expected: False\n# print(f'Dirty fields initially: {model.get_dirty_fields()}') # Expected: {}\n\n# model.characters = 'second value'\n# print(f'Is dirty after change? {model.is_dirty()}') # Expected: True\n# print(f'Dirty fields after change: {model.get_dirty_fields()}') # Expected: {'characters': 'first value'}\n\n# model.save()\n# print(f'Is dirty after save? {model.is_dirty()}') # Expected: False\n# model.boolean = False\n# print(f'Is dirty after another change? {model.is_dirty()}') # Expected: True","lang":"python","description":"To use django-dirtyfields, inherit from `DirtyFieldsMixin` in your Django model. Then, use `is_dirty()` to check if any fields have changed, or `get_dirty_fields()` to retrieve a dictionary of the original values for modified fields."},"warnings":[{"fix":"Be aware of this behavior when using Django 6.0+ and expressions. Review `get_dirty_fields()` output for fields that might be affected by Django's internal refresh mechanism.","message":"With Django 6.0+, fields that are expressions (e.g., `F()` objects) are now refreshed or deferred after `Model.save()`. This means such a field may incorrectly appear as 'dirty' if subsequently changed in-memory, even if its actual database value didn't change based on your application logic, potentially leading to unexpected behavior in `is_dirty()` or `get_dirty_fields()`.","severity":"breaking","affected_versions":"Django 6.0+"},{"fix":"After a transaction rollback, call `model.refresh_from_db()` on the instance to restore its correct state from the database. Alternatively, manually restore the in-memory fields that were edited.","message":"There's a limitation when using `django-dirtyfields` within database transactions. If a model instance is saved inside a `transaction.atomic()` block and the transaction is subsequently rolled back, the `is_dirty()` method might incorrectly return `False` (and `get_dirty_fields()` yield wrong results) because the instance's dirty state is reset after `save()` is called, irrespective of the transaction's eventual outcome.","severity":"gotcha","affected_versions":"All"},{"fix":"Consider defining your base model without `DirtyFieldsMixin` and create a proxy model that inherits from both your base model and `DirtyFieldsMixin` for scenarios where dirty field tracking is necessary.","message":"Using `DirtyFieldsMixin` introduces a small performance overhead as it needs to capture the model's state during initialization and saving to track changes. For models where dirty field tracking is not consistently needed, this overhead can be avoided by using a Proxy Model that inherits from `DirtyFieldsMixin` only when tracking is required.","severity":"gotcha","affected_versions":"All"},{"fix":"Account for signal execution when using `save_dirty_fields()`. If your signals rely on specific fields being dirty, ensure they are written to handle this, or consider alternative approaches if fine-grained signal control is needed.","message":"The `save_dirty_fields()` method, while only persisting the changed fields, still internally calls the standard `save()` method on the model instance. This means that any `pre_save` or `post_save` signals registered for your model will still be triggered.","severity":"gotcha","affected_versions":"All"}],"env_vars":null,"last_verified":"2026-04-13T00:00:00.000Z","next_check":"2026-07-12T00:00:00.000Z"}