Metadata-Version: 2.4
Name: sqlalchemy-soft-delete
Version: 0.1.0
Summary: Soft delete mixin for SQLAlchemy — never lose data again
Author-email: Shahab Rashidian Dezfuly <mm4heidary@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/shahabRDZ/sqlalchemy-soft-delete
Project-URL: Repository, https://github.com/shahabRDZ/sqlalchemy-soft-delete
Project-URL: Issues, https://github.com/shahabRDZ/sqlalchemy-soft-delete/issues
Keywords: sqlalchemy,soft-delete,mixin,orm,database
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: sqlalchemy>=2.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Dynamic: license-file

# sqlalchemy-soft-delete

[![PyPI version](https://badge.fury.io/py/sqlalchemy-soft-delete.svg)](https://pypi.org/project/sqlalchemy-soft-delete/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![SQLAlchemy 2.0+](https://img.shields.io/badge/SQLAlchemy-2.0+-red.svg)](https://www.sqlalchemy.org/)

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 `SoftDeleteMixin` to any model and you're done
- **`deleted_at` timestamp** — know exactly when a row was deleted
- **`is_deleted` hybrid property** — use in Python and in SQL queries
- **`soft_delete()` / `restore()`** — intuitive instance methods
- **`with_deleted()` helper** — bypass the filter when you need to see everything
- **SQLAlchemy 2.0+ native** — built on `Mapped`, `mapped_column`, and `hybrid_property`
- **Zero dependencies** beyond SQLAlchemy itself
- **Fully typed** — works great with mypy and IDE autocompletion

## Installation

```bash
pip install sqlalchemy-soft-delete
```

## Quick Start

### Before (hard delete)

```python
session.delete(user)  # Gone forever
session.commit()
```

### After (soft delete)

```python
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

```python
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

```python
user.restore()
session.commit()

print(user.is_deleted)   # False
print(user.deleted_at)   # None
```

### Querying only active rows

```python
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)

```python
# No filter — returns everything
stmt = select(User)
all_users = session.execute(stmt).scalars().all()
```

### Querying only deleted rows

```python
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](LICENSE) for details.
