sqlalchemy-soft-delete
A lightweight SQLAlchemy mixin that adds soft delete to any model. Instead of permanently removing rows, it sets a deleted_at timestamp — so you never lose data again.
Features
- Drop-in mixin — add
SoftDeleteMixinto any model and you're done deleted_attimestamp — know exactly when a row was deletedis_deletedhybrid property — use in Python and in SQL queriessoft_delete()/restore()— intuitive instance methodswith_deleted()helper — bypass the filter when you need to see everything- SQLAlchemy 2.0+ native — built on
Mapped,mapped_column, andhybrid_property - Zero dependencies beyond SQLAlchemy itself
- Fully typed — works great with mypy and IDE autocompletion
Installation
pip install sqlalchemy-soft-delete
Quick Start
Before (hard delete)
session.delete(user) # Gone forever
session.commit()
After (soft delete)
from sqlalchemy_soft_delete import SoftDeleteMixin
class User(SoftDeleteMixin, Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
# Soft delete — row stays in the database
user.soft_delete()
session.commit()
# Restore whenever you need
user.restore()
session.commit()
Usage
Soft deleting a row
user = session.get(User, 1)
user.soft_delete()
session.commit()
print(user.is_deleted) # True
print(user.deleted_at) # 2026-04-17 12:34:56+00:00
Restoring a soft-deleted row
user.restore()
session.commit()
print(user.is_deleted) # False
print(user.deleted_at) # None
Querying only active rows
from sqlalchemy import select
# Filter out deleted rows explicitly
stmt = select(User).where(~User.is_deleted)
active_users = session.execute(stmt).scalars().all()
Querying all rows (including deleted)
# No filter — returns everything
stmt = select(User)
all_users = session.execute(stmt).scalars().all()
Querying only deleted rows
stmt = select(User).where(User.is_deleted)
deleted_users = session.execute(stmt).scalars().all()
API Reference
SoftDeleteMixin
| Member | Type | Description |
|---|---|---|
deleted_at |
Mapped[datetime | None] |
UTC timestamp of deletion, None if active |
is_deleted |
hybrid_property |
True if soft-deleted; usable in Python and SQL |
soft_delete() |
method | Sets deleted_at to current UTC time |
restore() |
method | Clears deleted_at back to None |
with_deleted(query)
Marks a legacy Query object to include soft-deleted rows via execution options.
Requirements
- Python 3.10+
- SQLAlchemy 2.0+
License
MIT License. See LICENSE for details.
Release files for sqlalchemy-soft-delete 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sqlalchemy_soft_delete-0.1.0.tar.gz | 5.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sqlalchemy_soft_delete-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 11.5 kB
Release files / sqlalchemy_soft_delete-0.1.0.tar.gz
| Download URL | sqlalchemy_soft_delete-0.1.0.tar.gz |
|---|---|
| Size | 5.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1c6418fc03f634268de9ad900f2dc7c6f5c3c4b9b1b666511531946f7824b2ac
|
|
BLAKE2b-256 checksum How to use checksums |
22383b8abfd545bc31c464ef70df3ed1a8cbbd09a9fc2599bd3e31671566ed08
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / sqlalchemy_soft_delete-0.1.0-py3-none-any.whl
| Download URL | sqlalchemy_soft_delete-0.1.0-py3-none-any.whl |
|---|---|
| Size | 5.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f868dda19ef98130a10318cde5a37b90bf0834cfbb98ca065bbbc4a60c15f002
|
|
BLAKE2b-256 checksum How to use checksums |
84fdfa6dd85a0a413fd757b5b9966e73cc2a56bc1bb52c4c47568c00ccc28c46
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|