From 1bab9c492dd5cdb0c073f59baa0c97167dafa377 Mon Sep 17 00:00:00 2001 From: Michael van Tellingen Date: Sat, 1 Mar 2014 12:20:13 +0100 Subject: [PATCH 1/3] Add failing test --- model_utils/tests/tests.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index abad8e1..314aff6 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -1432,6 +1432,12 @@ class FieldTrackerTests(FieldTrackerTestCase, FieldTrackerCommonTests): self.assertEqual(in_db.number, self.instance.number) self.assertEqual(in_db.mutable, self.instance.mutable) + def test_with_deferred(self): + self.instance.name = 'new age' + self.instance.number = 1 + self.instance.save() + items = list(self.tracked_class.objects.only('name').all()) + class FieldTrackedModelCustomTests(FieldTrackerTestCase, FieldTrackerCommonTests): From 6ffae1ad8ffcfa6f9c3354e34f9ee5415e656f62 Mon Sep 17 00:00:00 2001 From: Michael van Tellingen Date: Sat, 1 Mar 2014 13:34:17 +0100 Subject: [PATCH 2/3] Add support for deferred fields in the FieldTracker --- AUTHORS.rst | 1 + model_utils/tests/tests.py | 14 +++++++++++++- model_utils/tracker.py | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 7ceed61..8da575b 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -16,6 +16,7 @@ Jannis Leidel Javier GarcĂ­a Sogo Jeff Elmore Keryn Knight +Michael van Tellingen Mikhail Silonov Patryk Zawadzki Paul McLanahan diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index 314aff6..fac85ac 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -1436,7 +1436,19 @@ class FieldTrackerTests(FieldTrackerTestCase, FieldTrackerCommonTests): self.instance.name = 'new age' self.instance.number = 1 self.instance.save() - items = list(self.tracked_class.objects.only('name').all()) + item = list(self.tracked_class.objects.only('name').all())[0] + self.assertTrue(item.tracker.deferred_fields) + + self.assertEqual(item.tracker.previous('number'), None) + self.assertTrue('number' in item.tracker.deferred_fields) + + self.assertEqual(item.number, 1) + self.assertTrue('number' not in item.tracker.deferred_fields) + self.assertEqual(item.tracker.previous('number'), 1) + self.assertFalse(item.tracker.has_changed('number')) + + item.number = 2 + self.assertTrue(item.tracker.has_changed('number')) class FieldTrackedModelCustomTests(FieldTrackerTestCase, diff --git a/model_utils/tracker.py b/model_utils/tracker.py index 77eaaa4..8c38cb5 100644 --- a/model_utils/tracker.py +++ b/model_utils/tracker.py @@ -4,6 +4,7 @@ from copy import deepcopy from django.db import models from django.core.exceptions import FieldError +from django.db.models.query_utils import DeferredAttribute class FieldInstanceTracker(object): @@ -11,6 +12,7 @@ class FieldInstanceTracker(object): self.instance = instance self.fields = fields self.field_map = field_map + self.init_deferred_fields() def get_field_value(self, field): return getattr(self.instance, self.field_map[field]) @@ -30,7 +32,14 @@ class FieldInstanceTracker(object): def current(self, fields=None): """Returns dict of current values for all tracked fields""" if fields is None: - fields = self.fields + if self.deferred_fields: + fields = [ + field for field in self.fields + if field not in self.deferred_fields + ] + else: + fields = self.fields + return dict((f, self.get_field_value(f)) for f in fields) def has_changed(self, field): @@ -52,6 +61,30 @@ class FieldInstanceTracker(object): if self.has_changed(field) ) + def init_deferred_fields(self): + self.deferred_fields = [] + if not self.instance._deferred: + return + + class DeferredAttributeTracker(DeferredAttribute): + def __get__(field, instance, owner): + data = instance.__dict__ + if data.get(field.field_name, field) is field: + self.deferred_fields.remove(field.field_name) + value = super(DeferredAttributeTracker, field).__get__( + instance, owner) + self.saved_data[field.field_name] = deepcopy(value) + return data[field.field_name] + + for field in self.fields: + field_obj = self.instance.__class__.__dict__.get(field) + if isinstance(field_obj, DeferredAttribute): + self.deferred_fields.append(field) + + field_tracker = DeferredAttributeTracker( + field_obj.field_name, None) + setattr(self.instance.__class__, field, field_tracker) + class FieldTracker(object): From 2bcddd5be9c0fbd942d9c66c087cf520745e92f6 Mon Sep 17 00:00:00 2001 From: Michael van Tellingen Date: Sat, 1 Mar 2014 14:24:25 +0100 Subject: [PATCH 3/3] Add fix for Django 1.4 --- model_utils/tracker.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/model_utils/tracker.py b/model_utils/tracker.py index 8c38cb5..6cb4355 100644 --- a/model_utils/tracker.py +++ b/model_utils/tracker.py @@ -81,8 +81,13 @@ class FieldInstanceTracker(object): if isinstance(field_obj, DeferredAttribute): self.deferred_fields.append(field) + # Django 1.4 + model = None + if hasattr(field_obj, 'model_ref'): + model = field_obj.model_ref() + field_tracker = DeferredAttributeTracker( - field_obj.field_name, None) + field_obj.field_name, model) setattr(self.instance.__class__, field, field_tracker)