Developer Interface


Decorator that wraps models with a save hook to save only what’s changed.


Decorator for specifying groups of fields to be updated together.

>>> from django.db import models
>>> from save_the_change.decorators import SaveTheChange, UpdateTogether
>>> @SaveTheChange
>>> @UpdateTogether(
...     ('height_feet', 'height_inches'),
...     ('weight_pounds', 'weight_kilos')
... )
>>> class Knight(models.model):
>>>     ...

Decorator that adds some methods and properties to models for working with changed fields.

True if any fields on the model have changed from its last known database representation.
A set of the names of all changed fields on the model.
The model’s fields in their last known database representation as a read-only mapping (OldValues).
Reverts the given fields back to their last known database representation.
class save_the_change.mappings.OldValues(instance)[source]

A read-only Mapping of the original values for its model.

Attributes can be accessed with either dot or bracket notation.


class save_the_change.decorators.STCMixin(*args, **kwargs)[source]

Hooks into __init__(), save(), and refresh_from_db(), and adds some new, private attributes to the model:

A dict storing a copy of potentially mutable values on first access.
A dict storing a copy of immutable fields’ original values when they’re changed.

Wraps model attributes in descriptors to track their changes.

Injects a mixin into the model’s __bases__ as well to handle the {create,load}/change/save lifecycle, and adds some attributes to the model’s _meta:

True if we’ve already wrapped fields on this model.
A list of hooks to run during save().
save_the_change.decorators._save_the_change_save_hook(instance, *args, **kwargs)[source]

Sets update_fields on save() to only what’s changed.

update_fields is only set if it doesn’t already exist and when doing so is safe. This means its not set if the instance is new and yet to be saved to the database, if the instance is being saved with a new primary key, or if save() has been called with force_insert.

Returns:(continue_saving, args, kwargs)
Return type:tuple
save_the_change.decorators._update_together_save_hook(instance, *args, **kwargs)[source]

Sets update_fields on save() to include any fields that have been marked as needing to be updated together with fields already in update_fields.

Returns:(continue_saving, args, kwargs)
Return type:tuple
class save_the_change.descriptors.ChangeTrackingDescriptor(name, django_descriptor=None)[source]

Descriptor that wraps model attributes to detect changes.

Not all fields in older versions of Django are represented by descriptors themselves, so we handle both getting/setting bare attributes on the model and calling out to descriptors if they exist.


Iterates over concrete fields in a model and wraps them in a descriptor to track their changes.


Checks if given object is likely mutable.

Parameters:obj – object to check.

We check that the object is itself a known immutable type, and then attempt to recursively check any objects within it. Strings are special cased to prevent us getting stuck in an infinite loop.

Returns:True if the object is likely mutable, False if it definitely is not.
Return type:bool