diff --git a/model_utils/fields.py b/model_utils/fields.py index 989c166..49a3185 100644 --- a/model_utils/fields.py +++ b/model_utils/fields.py @@ -33,14 +33,26 @@ class AutoLastModifiedField(AutoCreatedField): By default, sets editable=False and default=datetime.now. """ + def get_default(self): + """Return the default value for this field.""" + if not hasattr(self, "_default"): + self._default = self._get_default() + return self._default def pre_save(self, model_instance, add): value = now() - if not model_instance.pk: - for field in model_instance._meta.get_fields(): - if isinstance(field, AutoCreatedField): - value = getattr(model_instance, field.name) - break + if add: + current_value = getattr(model_instance, self.attname, self.get_default()) + if current_value != self.get_default(): + # when creating an instance and the modified date is set + # don't change the value, assume the developer wants that + # control. + value = getattr(model_instance, self.attname) + else: + for field in model_instance._meta.get_fields(): + if isinstance(field, AutoCreatedField): + value = getattr(model_instance, field.name) + break setattr(model_instance, self.attname, value) return value diff --git a/tests/test_models/test_timestamped_model.py b/tests/test_models/test_timestamped_model.py index cac07f3..9fc884f 100644 --- a/tests/test_models/test_timestamped_model.py +++ b/tests/test_models/test_timestamped_model.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from datetime import datetime +from datetime import datetime, timedelta from freezegun import freeze_time @@ -30,3 +30,66 @@ class TimeStampedModelTests(TestCase): t1.save() self.assertEqual(t1.modified, datetime(2016, 1, 2)) + + def test_overriding_created_via_object_creation_also_uses_creation_date_for_modified(self): + """ + Setting the created date when first creating an object + should be permissable. + """ + different_date = datetime.today() - timedelta(weeks=52) + t1 = TimeStamp.objects.create(created=different_date) + self.assertEqual(t1.created, different_date) + self.assertEqual(t1.modified, different_date) + + + def test_overriding_modified_via_object_creation(self): + """ + Setting the modified date explicitly should be possible when + first creating an object, but not thereafter. + """ + different_date = datetime.today() - timedelta(weeks=52) + t1 = TimeStamp.objects.create(modified=different_date) + self.assertEqual(t1.modified, different_date) + self.assertNotEqual(t1.created, different_date) + + def test_overriding_created_after_object_created(self): + """ + The created date may be changed post-create + """ + t1 = TimeStamp.objects.create() + different_date = datetime.today() - timedelta(weeks=52) + t1.created = different_date + t1.save() + self.assertEqual(t1.created, different_date) + + def test_overriding_modified_after_object_created(self): + """ + The modified date should always be updated when the object + is saved, regardless of attempts to change it. + """ + t1 = TimeStamp.objects.create() + different_date = datetime.today() - timedelta(weeks=52) + t1.modified = different_date + t1.save() + self.assertNotEqual(t1.modified, different_date) + + def test_overrides_using_save(self): + """ + The first time an object is saved, allow modification of both + created and modified fields. + After that, only created may be modified manually. + """ + t1 = TimeStamp() + different_date = datetime.today() - timedelta(weeks=52) + t1.created = different_date + t1.modified = different_date + t1.save() + self.assertEqual(t1.created, different_date) + self.assertEqual(t1.modified, different_date) + different_date2 = datetime.today() - timedelta(weeks=26) + t1.created = different_date2 + t1.modified = different_date2 + t1.save() + self.assertEqual(t1.created, different_date2) + self.assertNotEqual(t1.modified, different_date2) + self.assertNotEqual(t1.modified, different_date)