Skip to content
Go back

SQLAlchemy's TypeDecorator

Published:  at  18:05

A class used in a recent project showing the use of TypeDecorator from sqlalchemy. TypeDecorator facilitates translation behaviour between Python types and database values. In this case, it ensures that all datetime fields using this type are stored and retrieved in UTC, providing consistent timezone handling across the application.

More info from SQLAlchemy docs

class UTCDateTime(TypeDecorator):
    """Custom SQLAlchemy type that automatically converts datetimes to UTC.

    This type decorator ensures all datetime values are stored in UTC timezone
    in the database, providing consistent timezone handling across the application.

    Attributes:
        impl: The underlying DateTime type implementation.
        cache_ok: Whether this type is safe to cache.
    """

    impl = DateTime
    cache_ok = True

    @property
    def python_type(self):
        """Return the Python type object expected for this type."""
        return dt.datetime

    def process_bind_param(self, value, _dialect):
        """Process the binding parameter and convert to UTC timezone if not None.

        Args:
            value (datetime): The datetime value to be processed.
            _dialect: The SQL dialect (unused).

        Returns:
            datetime | None: The UTC timezone converted datetime if value is not None.
        """
        if value is not None:
            return value.astimezone(dt.UTC)
        return value

    def process_result_value(self, value, _dialect):
        """Process result values - ensures timezone awareness."""
        if value is not None and value.tzinfo is None:
            return value.replace(tzinfo=dt.UTC)
        return value

Usage:

class DBBase(DeclarativeBase):
    """Base class for all database models.

    Provides common attributes for tracking record creation and modification times.
    All model classes should inherit from this base class to maintain consistent
    timestamp tracking.

    Attributes:
        created: Timestamp when the record was created, stored in UTC.
        modified: Timestamp when the record was last modified, stored in UTC.
    """

    __abstract__ = True

    created: Mapped[dt.datetime] = mapped_column(
        UTCDateTime, insert_default=func.now(), nullable=False
    )
    modified: Mapped[dt.datetime | None] = mapped_column(
        UTCDateTime, nullable=True
    )


Previous Post
Monorepo with uv workspaces