sqlalchemy-utils
SQLAlchemy-Utils is a broad add-on toolbox for SQLAlchemy. It provides custom column types for choices, email addresses, URLs, IPs, UUIDs, passwords, encrypted strings, phone numbers, colors, locales, ranges, and search vectors; helpers for creating and inspecting databases; ORM inspection functions; automatic coercion listeners; timestamp and repr mixins; aggregate attributes; generic relationships; and database views. You adopt individual pieces, not a replacement ORM.
Still valuable when several of its types and ORM helpers remove real repeated work. Install it selectively and with explicit extras; for one utility or an async-only service, local SQLAlchemy 2 code is usually easier to reason about.
Use it if
- Your SQLAlchemy models repeatedly need value-object column types such as ChoiceType, URLType, PasswordType, UUIDType, or PhoneNumberType
- Your test or administration code needs portable database_exists, create_database, or drop_database helpers
- You need mapper inspection, deterministic ordering, change detection, generic repr, timestamp, aggregate, or view utilities already implemented against SQLAlchemy internals
- You accept optional dependencies and global mapper listeners to gain automatic coercion from scalar assignment to richer Python values
- You need only one small helper that modern SQLAlchemy or Python already covers: this package is a wide toolbox whose maintenance and transitive policy may exceed the saved code
- You use an async-only SQLAlchemy stack: database creation helpers construct synchronous engines internally, and many examples and listener patterns are written around the synchronous ORM
- You cannot install type-specific extras: URLType, PasswordType, PhoneNumberType, ColorType, ArrowType, encrypted types, and others depend on separate packages listed as optional extras
- You require database-enforced validation: many custom types serialize to ordinary text or binary columns and rely on Python coercion, so other writers can bypass the intended domain rule
- You are considering EncryptedType from an old example: the current data-type docs mark it deprecated since 0.36.6 and direct new code to StringEncryptedType
Setup reality
`pip install sqlalchemy-utils` installs SQLAlchemy-Utils and requires SQLAlchemy 1.4 or newer on Python 3.9 or newer. That base install does not include most libraries behind richer types. PyPI defines extras such as `password` for passlib, `url` for furl, `phone` for phonenumbers, `color` for colour, `arrow`, `pendulum`, `babel`, `timezone`, `intervals`, and `encrypted` for cryptography. A model can import successfully and then fail when a chosen type is constructed or return plain strings instead of value objects when its optional library is absent, so declare the exact extras your models use. Automatic coercion is opt-in through `force_auto_coercion()` and must be called before models are configured; without it, assignment-time validation and convenient value objects may not happen. With no mapper argument it attaches a listener to every SQLAlchemy mapper in the process, which can surprise applications that host multiple model sets or test suites. Custom types still need Alembic review: their physical storage is commonly String, Unicode, Text, or binary, and autogenerated migrations may need explicit rendering rules. PasswordType needs passlib scheme policy and migration planning; changing hashes can occur during comparison. StringEncryptedType needs durable key management and does not turn a column into a searchable secure vault. Database create and drop helpers require administrative credentials, open synchronous engines, and have backend-specific behavior; never put drop_database on an application request path. The docs claim full create support for MySQL, PostgreSQL, and SQLite, with other engines more tentative. Pin SQLAlchemy and run mapper, migration, and target-database tests because this package necessarily touches ORM and dialect internals.
Patterns
Check whether a database existscheck-database
from sqlalchemy_utils import database_exists
url = 'postgresql+psycopg://admin:secret@localhost/app_test'
if not database_exists(url):
raise RuntimeError('test database is missing')The helper opens a synchronous engine and needs a driver plus enough server access to query database metadata. Do not log credential-bearing URLs.
Create a database for testscreate-test-database
from sqlalchemy_utils import create_database, database_exists
if not database_exists(test_url):
create_database(test_url, encoding='utf8')The credentials must be allowed to create databases. Concurrent workers can race this check, so provision unique database names or serialize setup.
Drop an isolated test databasedrop-test-database
from sqlalchemy_utils import database_exists, drop_database
if database_exists(test_url):
drop_database(test_url)This is destructive and backend-specific. Validate that test_url points to a disposable database before calling it.
Map an Enum with ChoiceTypechoice-column
from enum import Enum
import sqlalchemy as sa
from sqlalchemy_utils import ChoiceType
class Role(Enum):
admin = 'admin'
member = 'member'
role = sa.Column(ChoiceType(Role, impl=sa.String(20)), nullable=False)ChoiceType stores the enum value through the chosen implementation. Database-level allowed-value constraints still need an explicit constraint or native enum.
Normalize an email columnemail-column
import sqlalchemy as sa
from sqlalchemy_utils import EmailType
email = sa.Column(EmailType, nullable=False, unique=True)EmailType lowercases values; it does not verify deliverability or replace a database uniqueness strategy for case and collation rules.
Expose URL values as furl objectsurl-column
import sqlalchemy as sa
from sqlalchemy_utils import URLType, force_auto_coercion
force_auto_coercion()
website = sa.Column(URLType)
# Install sqlalchemy-utils[url] so assigned strings coerce to furl values.Without the furl optional dependency, URLType stores and returns strings, so the richer object behavior is not guaranteed by the base install.
Hash and verify passwordspassword-column
import sqlalchemy as sa
from sqlalchemy_utils import PasswordType, force_auto_coercion
force_auto_coercion()
password = sa.Column(PasswordType(
schemes=['pbkdf2_sha512'],
), nullable=False)
user.password = plaintext
assert user.password == plaintextInstall sqlalchemy-utils[password]. Choose schemes through a reviewed password policy and never compare or serialize the stored hash as ordinary text.
Enable assignment-time value coercionenable-coercion
from sqlalchemy_utils import force_auto_coercion
force_auto_coercion()
# Define or import mapped model classes after this call.With no mapper argument this attaches to all SQLAlchemy mappers in the process. Call it once before mapper configuration, not repeatedly per request.
Add created and updated timestampstimestamp-model
import sqlalchemy as sa
from sqlalchemy_utils import Timestamp
class Article(Timestamp, Base):
__tablename__ = 'article'
id = sa.Column(sa.Integer, primary_key=True)The mixin stores naive UTC datetime values and updates through an ORM before_update event; bulk SQL updates can bypass instance events.
Generate a safe model reprmodel-repr
from sqlalchemy_utils import generic_repr
@generic_repr('id', 'email')
class User(Base):
__tablename__ = 'user'
# mapped columns hereList only non-secret fields. The helper avoids loading deferred fields, but including password hashes or tokens still leaks them into logs.
Escape user text for a LIKE patternescape-like-query
from sqlalchemy import select
from sqlalchemy_utils import escape_like
needle = escape_like(user_text)
stmt = select(User).where(User.name.ilike(f'%{needle}%', escape='*'))Pass the same escape character to SQLAlchemy's LIKE expression that escape_like used, or wildcard characters will not be treated literally.
Inspect mapped columns consistentlyinspect-columns
from sqlalchemy_utils import get_columns
for column in get_columns(User):
print(column.key, column.type)get_columns accepts several SQLAlchemy objects, but code that depends on mapper metadata should still be covered by tests across SQLAlchemy upgrades.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| sqlalchemy | PyPI | Modern built-ins and a few local TypeDecorator or inspection helpers cover your needs without another compatibility layer |
| sqlmodel | PyPI | You want Pydantic-style typed models integrated with SQLAlchemy rather than a bag of ORM utilities |
| sqlalchemy-mixins | PyPI | Your main need is active-record-style CRUD, serialization, smart querying, and model mixins |