diff --git a/docs/index.rst b/docs/index.rst index 02a672f..da5bdd9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,4 +19,5 @@ SQLAlchemy-Utils provides custom data types and various utility functions for SQ foreign_key_helpers orm_helpers utility_classes + models license diff --git a/docs/models.rst b/docs/models.rst new file mode 100644 index 0000000..dd304c3 --- /dev/null +++ b/docs/models.rst @@ -0,0 +1,10 @@ +Listeners +========= + + +Timestamp +^^^^^^^^^ + +.. module:: sqlalchemy_utils.models.Timestamp + +.. autoclass:: Timestamp diff --git a/sqlalchemy_utils/__init__.py b/sqlalchemy_utils/__init__.py index bab879b..11e1e99 100644 --- a/sqlalchemy_utils/__init__.py +++ b/sqlalchemy_utils/__init__.py @@ -70,6 +70,7 @@ from .types import ( UUIDType, WeekDaysType ) +from .models import Timestamp __version__ = '0.26.5' @@ -139,9 +140,10 @@ __all__ = ( QueryChain, ScalarListException, ScalarListType, + Timestamp, TimezoneType, TSVectorType, URLType, UUIDType, - WeekDaysType + WeekDaysType, ) diff --git a/sqlalchemy_utils/models.py b/sqlalchemy_utils/models.py new file mode 100644 index 0000000..6fec32c --- /dev/null +++ b/sqlalchemy_utils/models.py @@ -0,0 +1,29 @@ +from datetime import datetime +import sqlalchemy as sa + + +class Timestamp(object): + """Adds `created` and `updated` columns to a derived declarative model. + + The `created` column is handled through a default and the `updated` + column is handled through a `before_update` event that propagates + for all derived declarative models. + + :: + import sqlalchemy as sa + from sqlalchemy_utils import Timestamp + + class SomeModel(Base, Timestamp): + __tablename__ = 'somemodel' + id = sa.Column(sa.Integer, primary_key=True) + """ + + created = sa.Column(sa.DateTime, default=datetime.utcnow, nullable=False) + updated = sa.Column(sa.DateTime, default=datetime.utcnow, nullable=False) + + +@sa.event.listens_for(Timestamp, 'before_update', propagate=True) +def timestamp_before_update(mapper, connection, target): + # When a model with a timestamp is updated; force update the updated + # timestamp. + target.updated = datetime.utcnow() diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 0000000..5968e1e --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,37 @@ +from datetime import datetime +import sqlalchemy as sa +from sqlalchemy_utils import Timestamp +from tests import TestCase + + +class TestTimestamp(TestCase): + + def create_models(self): + class Article(self.Base, Timestamp): + __tablename__ = 'article' + id = sa.Column(sa.Integer, primary_key=True) + name = sa.Column(sa.Unicode(255), default=u'Some article') + + self.Article = Article + + def test_created(self): + then = datetime.utcnow() + article = self.Article() + + self.session.add(article) + self.session.commit() + + assert article.created >= then and article.created <= datetime.utcnow() + + def test_updated(self): + article = self.Article() + + self.session.add(article) + self.session.commit() + + then = datetime.utcnow() + article.name = u"Something" + + self.session.commit() + + assert article.updated >= then and article.updated <= datetime.utcnow()