{"id":1998,"library":"django-model-utils","title":"Django Model Utils","description":"Django Model Utils is a comprehensive Python library that provides a collection of abstract base classes, managers, fields, and utilities designed to simplify common Django model patterns. It includes features like `StatusField` for managing object states, `TimeStampedModel` for automatic creation/modification timestamps, `SoftDeletableModel` for logical deletion, and `FieldTracker` for monitoring field changes. The current version is 5.0.0, and it generally keeps pace with Django's major release cycle for compatibility.","status":"active","version":"5.0.0","language":"en","source_language":"en","source_url":"https://github.com/jazzband/django-model-utils","tags":["django","models","mixins","utilities","field-tracking","status-management","soft-delete","timestamps"],"install":[{"cmd":"pip install django-model-utils","lang":"bash","label":"Install stable version"}],"dependencies":[{"reason":"Core framework dependency. Supports Django >=3.2 and Python >=3.8. Version 5.0.0 formally supports Django up to 5.1.","package":"Django","optional":false}],"imports":[{"note":"Used for tracking changes to model fields.","symbol":"FieldTracker","correct":"from model_utils import FieldTracker"},{"note":"A CharField subclass for models with predefined states.","symbol":"StatusField","correct":"from model_utils.fields import StatusField"},{"note":"Abstract base class providing 'created' and 'modified' DateTimeFields.","symbol":"TimeStampedModel","correct":"from model_utils.models import TimeStampedModel"},{"note":"Abstract base class for models with logical deletion instead of actual removal.","symbol":"SoftDeletableModel","correct":"from model_utils.models import SoftDeletableModel"},{"note":"Utility for defining model field choices with conveniences.","symbol":"Choices","correct":"from model_utils import Choices"}],"quickstart":{"code":"from django.db import models\nfrom model_utils import FieldTracker\n\nclass Post(models.Model):\n    title = models.CharField(max_length=100)\n    body = models.TextField()\n    tracker = FieldTracker()\n\n    def save(self, *args, **kwargs):\n        # Example of using FieldTracker in a save method\n        super().save(*args, **kwargs)\n        if self.tracker.has_changed('title'):\n            print(f\"Title changed from '{self.tracker.previous('title')}' to '{self.title}'\")\n\n# Example usage (requires Django setup and database interaction)\n# post = Post.objects.create(title='Initial Title', body='Some body content')\n# post.title = 'Updated Title'\n# post.save()\n# print(post.tracker.changed()) # Shows all changed fields and their previous values\n","lang":"python","description":"This quickstart demonstrates how to use `FieldTracker` to monitor changes in a model's fields. The `tracker` instance on the model allows you to check if a specific field has changed (`has_changed()`), retrieve its previous value (`previous()`), or get a dictionary of all changed fields and their old values (`changed()`). The example shows how to integrate this logic within a custom `save` method."},"warnings":[{"fix":"Consider using Django's built-in `bulk_create` or `update` methods which bypass signals, or implement custom signal dispatching logic. For more complex scenarios, you might need to temporarily disconnect signals before saving and reconnect them afterwards.","message":"The `SaveSignalHandlingModel` has been removed. If you used this abstract base class to temporarily disable signals during a save operation, you will need to refactor your code. This model was removed in version 4.4.0 due to maintainability concerns with its modified `Model.save_base()` method.","severity":"breaking","affected_versions":">=4.4.0"},{"fix":"Update your code to no longer rely on `get_quoted_query()`. If you were using `JoinManager` or `JoinManagerMixin`, these were deprecated in 5.0.0; migrate to `JoinQueryset.as_manager()` instead.","message":"The `JoinQueryset.get_quoted_query()` method was removed in version 4.5.1. If your code directly invoked this method, it will now fail.","severity":"breaking","affected_versions":">=4.5.1"},{"fix":"Ensure your project runs on Python 3.x (specifically `>=3.8` for `django-model-utils` 5.0.0) and uses Django 2.2 or later (preferably >=3.2 as per 4.3.1 requirements).","message":"Version 4.0.0 dropped support for Python 2.7 and Django 1.11. Additionally, it removed internal usage of the `six` compatibility library.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"Review existing code using `FieldTracker` within custom `save()` methods, signal handlers, or after `refresh_from_db()` to ensure the new behavior aligns with expectations. Specifically, logic relying on `tracker.changed()` within `post_save` might need adjustment if it expects changes that occurred during `pre_save` to be re-evaluated.","message":"Behavioral changes to `FieldTracker` in version 4.1.0 mean it now correctly marks fields as not changed after `refresh_from_db` and respects `update_fields` when saving. It also resets states after `pre_save()` signals instead of `save()` signals.","severity":"breaking","affected_versions":">=4.1.0"},{"fix":"Replace all instances of `ModelTracker` with `FieldTracker` to avoid performance issues and leverage improved `ForeignKey` handling.","message":"The `ModelTracker` was deprecated in favor of `FieldTracker` due to serious flaws in handling `ForeignKey` fields, leading to potentially many extra database queries.","severity":"deprecated","affected_versions":"All versions (deprecated in 1.3.0)"},{"fix":"Always use the `available_objects` manager to retrieve only non-deleted instances, or `all_objects` when you explicitly want to include deleted instances.","message":"For `SoftDeletableModel`, relying on the default `objects` manager to filter out not-deleted instances is deprecated. The `objects` manager will include deleted objects in a future release.","severity":"deprecated","affected_versions":"All versions (deprecated since 4.1.0, mentioned in 5.0.0 docs)"},{"fix":"If your `MonitorField` relies on `django.utils.timezone.now` for nullable fields without an explicit default, you must now set `default=django.utils.timezone.now` explicitly to maintain the previous behavior.","message":"In version 5.0.0, the `MonitorField` now defaults to `None` when nullable and no default is provided, instead of `django.utils.timezone.now`.","severity":"gotcha","affected_versions":">=5.0.0"}],"env_vars":null,"last_verified":"2026-04-09T00:00:00.000Z","next_check":"2026-07-08T00:00:00.000Z"}