From 2d018ca8475199ccbdcb10b0be94f2cbf4555130 Mon Sep 17 00:00:00 2001 From: Carl Meyer Date: Thu, 2 Jul 2009 16:14:29 -0400 Subject: [PATCH] added TimeStampedModel --HG-- extra : convert_revision : carl%40dirtcircle.com-20090702201429-hph32omqgfgrga52 --- README.txt | 21 ++++++++++++++++++++- model_utils/fields.py | 29 +++++++++++++++++++++++++++++ model_utils/models.py | 16 ++++++++++++++++ model_utils/tests/models.py | 4 +++- model_utils/tests/tests.py | 15 ++++++++++++++- 5 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 model_utils/fields.py diff --git a/README.txt b/README.txt index 4ad0d5a..8f222a9 100644 --- a/README.txt +++ b/README.txt @@ -22,10 +22,29 @@ But when you iterate over ``nearby_places``, you'll get only ``Place`` instances back, even for objects that are "really" ``Restaurant`` or ``Bar``. If you have ``Place`` inherit from ``InheritanceCastModel``, you can just call the ``cast()`` method on each ``Place`` and it will -return an instance of the proper subtype, ``Restaurant`` or ``Bar``. +return an instance of the proper subtype, ``Restaurant`` or ``Bar``:: + + from model_utils.models import InheritanceCastModel + + class Place(InheritanceCastModel): + ... + + class Restaurant(Place): + ... + + nearby_places = Place.objects.filter(location='here') + for place in nearby_places: + restaurant_or_bar = place.cast() + ... .. note:: This is inefficient for large querysets, as it results in n queries to the subtype tables. It would be possible to write a QuerySet subclass that could reduce this to k queries, where there are k subtypes in the inheritance tree. + +TimeStampedModel +================ + +This abstract base class just provides self-updating ``created`` and +``modified`` fields on any model that inherits it. diff --git a/model_utils/fields.py b/model_utils/fields.py new file mode 100644 index 0000000..73a1ad8 --- /dev/null +++ b/model_utils/fields.py @@ -0,0 +1,29 @@ +from datetime import datetime + +from django.db import models + +class AutoCreatedField (models.DateTimeField): + """ + A DateTimeField that automatically populates itself at + object creation. + + By default, sets editable=False, default=datetime.now. + + """ + def __init__ (self, *args, **kwargs): + kwargs.setdefault('editable', False) + kwargs.setdefault('default', datetime.now) + super(AutoCreatedField, self).__init__(*args, **kwargs) + +class AutoLastModifiedField (AutoCreatedField): + """ + A DateTimeField that updates itself on each save() of the model. + + By default, sets editable=False and default=datetime.now. + + """ + def pre_save (self, model_instance, add): + value = datetime.now() + setattr(model_instance, self.attname, value) + return value + diff --git a/model_utils/models.py b/model_utils/models.py index c566d38..0939a1f 100644 --- a/model_utils/models.py +++ b/model_utils/models.py @@ -1,6 +1,8 @@ from django.db import models from django.contrib.contenttypes.models import ContentType +from model_utils.fields import AutoCreatedField, AutoLastModifiedField + class InheritanceCastModel(models.Model): """ An abstract base class that provides a ``real_type`` FK to ContentType. @@ -24,3 +26,17 @@ class InheritanceCastModel(models.Model): class Meta: abstract = True + + +class TimeStampedModel (models.Model): + """ + An abstract base class model that provides self-updating + ``created`` and ``modified`` fields. + + """ + created = AutoCreatedField() + modified = AutoLastModifiedField() + + class Meta: + abstract = True + diff --git a/model_utils/tests/models.py b/model_utils/tests/models.py index c10c3f3..83fe9af 100644 --- a/model_utils/tests/models.py +++ b/model_utils/tests/models.py @@ -1,6 +1,6 @@ from django.db import models -from model_utils.models import InheritanceCastModel +from model_utils.models import InheritanceCastModel, TimeStampedModel class InheritParent(InheritanceCastModel): pass @@ -8,3 +8,5 @@ class InheritParent(InheritanceCastModel): class InheritChild(InheritParent): pass +class TimeStamp(TimeStampedModel): + pass diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index 5da2d5c..3ddde4a 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -1,7 +1,7 @@ from django.test import TestCase from django.contrib.contenttypes.models import ContentType -from model_utils.tests.models import InheritParent, InheritChild +from model_utils.tests.models import InheritParent, InheritChild, TimeStamp class InheritanceCastModelTests(TestCase): def setUp(self): @@ -19,3 +19,16 @@ class InheritanceCastModelTests(TestCase): def testCast(self): obj = InheritParent.objects.get(pk=self.child.pk).cast() self.assertEquals(obj.__class__, InheritChild) + + +class TimeStampedModelTests(TestCase): + def testCreated(self): + t1 = TimeStamp.objects.create() + t2 = TimeStamp.objects.create() + self.assert_(t2.created > t1.created) + + def testModified(self): + t1 = TimeStamp.objects.create() + t2 = TimeStamp.objects.create() + t1.save() + self.assert_(t2.modified < t1.modified)