| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 |
- from django.db.models.query_utils import DeferredAttribute
- from netbox.registry import registry
- class Tracker:
- """
- An ephemeral instance employed to record which tracked fields on an instance have been modified.
- """
- def __init__(self):
- self._changed_fields = {}
- def __contains__(self, item):
- return item in self._changed_fields
- def set(self, name, value):
- """
- Mark an attribute as having been changed and record its original value.
- """
- self._changed_fields[name] = value
- def get(self, name):
- """
- Return the original value of a changed field. Raises KeyError if name is not found.
- """
- return self._changed_fields[name]
- def clear(self, *names):
- """
- Clear any fields that were recorded as having been changed.
- """
- for name in names:
- self._changed_fields.pop(name, None)
- else:
- self._changed_fields = {}
- class TrackingModelMixin:
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- # Mark the instance as initialized, to enable our custom __setattr__()
- self._initialized = True
- @property
- def tracker(self):
- """
- Return the Tracker instance for this instance, first creating it if necessary.
- """
- if not hasattr(self._state, "_tracker"):
- self._state._tracker = Tracker()
- return self._state._tracker
- def save(self, *args, **kwargs):
- super().save(*args, **kwargs)
- # Clear any tracked fields now that changes have been saved
- update_fields = kwargs.get('update_fields', [])
- self.tracker.clear(*update_fields)
- def __setattr__(self, name, value):
- if hasattr(self, "_initialized"):
- # Record any changes to a tracked field
- if name in registry['counter_fields'][self.__class__]:
- if name not in self.tracker:
- # The attribute has been created or changed
- if name in self.__dict__:
- old_value = getattr(self, name)
- if value != old_value:
- self.tracker.set(name, old_value)
- else:
- self.tracker.set(name, DeferredAttribute)
- elif value == self.tracker.get(name):
- # A previously changed attribute has been restored
- self.tracker.clear(name)
- super().__setattr__(name, value)
|