Ferrum
A next-generation async ORM for Python. Rust-powered engine. Pydantic-native models. Django-inspired developer experience.
Ferrum is an async-first ORM designed for modern Python applications.
Built around a Rust-powered core and a Python-native API, Ferrum combines the ergonomics of Django's ORM, the type safety of Pydantic, and the performance of Rust.
Why Ferrum?
Existing Python ORMs often force developers to choose between:
- Developer experience
- Async support
- Type safety
- Performance
Ferrum aims to provide all four.
Goals
- Native async from day one
- Pydantic-first models
- Django-inspired ORM experience
- Rust-powered query engine
- PostgreSQL-first architecture (MySQL, SQLite, and SQL Server via optional extras)
- Type-safe query construction
- Automatic migrations
- High-performance result hydration
- Production-ready observability
Quick Example
from ferrum import Model
class User(Model):
id: int
email: str
is_active: bool = True
user = await User.objects.create(
conn,
email="john@example.com",
)
# Insert from an instance (sentinel PK is dropped so the DB default runs)
draft = User(id=0, email="dana@example.com")
created = await User.objects.create(conn, draft)
# Persist one instance's changes by primary key
created.is_active = False
await User.objects.update_instance(conn, created, fields=["is_active"])
users = await (
User.objects
.filter(is_active=True)
.order_by("-id")
.limit(10)
.all(conn)
)
async with conn.transaction() as tx:
user = await User.objects.create(tx, email="jane@example.com")
await AuditLog.objects.create(tx, user_id=user.id, action="signup")
Features
Async First
No synchronous compatibility layer.
Ferrum is designed around modern async Python applications.
users = await User.objects.all(conn)
Pydantic Native
Models are built directly on top of Pydantic.
class User(Model):
id: int
email: str
No duplicate schema definitions.
Field Options
ferrum.Field() accepts column-level constraints that are carried through to DDL:
| Parameter | Type | Description |
|---|---|---|
db_default |
str | None |
DB-side DEFAULT expression (e.g. "NOW()", "''", "GEN_RANDOM_UUID()"). Token casing is normalised (now() → NOW()). |
nullable |
bool | None |
Override annotation-derived nullability. Use nullable=False to force NOT NULL even on a T | None-annotated field. |
db_index |
bool |
Emit a CREATE INDEX on this column (auto-named idx_{table}_{field}). |
unique |
bool |
Emit a UNIQUE constraint. |
db_column |
str | None |
Override the database column name. |
primary_key |
bool |
Declare this field as the primary key. |
uuid_generate |
"v4" | "v7" | None |
Set GEN_RANDOM_UUID() / UUIDV7() as the DB default for UUID PK columns. |
max_length |
int | None |
Emit VARCHAR(n) instead of TEXT. |
max_digits / decimal_places |
int | None |
Emit NUMERIC(p,s). |
vector_dimensions |
int | None |
Required for Vector columns; emits VECTOR(n). |
jsonb_list |
bool |
Store list[T] as JSONB instead of a PostgreSQL ARRAY. |
generated |
bool |
Select/hydrate the DB-generated column, but exclude it from writes. |
read_only |
bool |
Select/hydrate the column, but reject explicit write assignments. |
from datetime import datetime
from typing import Annotated
from ferrum import Model, Field
class Event(Model):
id: int
# DB-side timestamp default; Python annotation allows None but column is NOT NULL
created_at: Annotated[datetime | None, Field(db_default="now()", nullable=False)]
# Indexed text column
kind: Annotated[str, Field(db_index=True, max_length=64)]
makemigrations honours these field attributes:
db_default/nullableare written intoCREATE TABLEandADD COLUMNDDL.db_index=TrueemitsCREATE INDEXafter the table op.- For already-existing tables,
makemigrationsnow autodiffs index and column attribute changes against the state projected from prior migration files:- Adding
db_index=Trueto an existing field →AddIndexop. - Removing
db_index=True→DropIndexop. - Adding or changing
db_default→AlterColumn … SET DEFAULT …op. - Removing
db_default→AlterColumn … DROP DEFAULTop. - Changing nullability →
AlterColumn … SET / DROP NOT NULLop (SET NOT NULL is classified destructive and requires--confirm). - Column type changes, renames, and drops remain manual (out of scope for v0.1).
- Adding
Django-Inspired API
Familiar query interface.
users = await (
User.objects
.filter(email__contains="@gmail.com")
.order_by("-created_at")
.all(conn)
)
Typed aggregates and bounded PostgreSQL streaming use the same metadata allowlists and bound-parameter compiler path:
from ferrum.queryset import Aggregate
counts = await (
Ticket.objects
.group_by("team_id")
.date_trunc("created_at", "day", alias="day")
.having(total__gte=10)
.aggregate(conn, total=Aggregate.count())
)
async with Ticket.objects.filter(active=True).stream(conn, chunk_size=500) as chunks:
async for tickets in chunks:
await process(tickets)
stream() preserves projection/deferred/value-queryset materialization and
deterministically releases its cursor on exhaustion, early break, cancellation, or
error. It rejects prefetch_related() because cross-chunk relationship batching is
not safe. Filtered update_returning() provides atomic compare-and-set semantics:
encode the expected state in filter() and treat an empty returned list as a lost race.
First-Class IDE Support
Ferrum ships a PEP 561 py.typed marker, so editors and type checkers (mypy, pyright,
ty) resolve its inline hints out of the box. Model.objects is typed as
QuerySet[YourModel], chaining preserves the model type, and terminals infer precise results —
no casts:
users: list[User] = await User.objects.filter(is_active=True).all(conn) # list[User]
user: User | None = await User.objects.first(conn) # User | None
rows: list[dict[str, Any]] = await User.objects.values("id", "email").all(conn)
ids: list[Any] = await User.objects.values_list("id", flat=True).all(conn)
values() / values_list() return dedicated ValuesQuerySet, ValuesListQuerySet, and
FlatValuesListQuerySet variants (all exported from ferrum) so all() returns
list[dict[str, Any]], list[tuple[Any, ...]], or list[Any] respectively.
Rust-Powered Core
Performance-critical components are implemented in Rust:
- Query compilation
- SQL generation
- Result decoding
- Schema analysis
- Migration planning
This allows Ferrum to maintain a Pythonic API without sacrificing performance.
Cross-Driver Full-Text Search
Native full-text search across PostgreSQL, MySQL, SQLite FTS5, and SQL Server — one QuerySet API, dialect-specific SQL emit and migration DDL.
Query modes (filter lookups and ranking):
| Mode | Lookup operator | Typical use |
|---|---|---|
plain |
__match |
Natural-language terms |
phrase |
__match_phrase |
Exact phrase |
websearch |
__match_websearch |
Web-style quotes, - negation |
boolean |
__match_boolean |
Boolean operators (&, |, !) |
Convenience methods:
# Filter + relevance ranking in one call
hits = await Article.objects.search(
"python async orm", field="body", mode="websearch"
).limit(10).all(conn)
# Rank without an implicit filter
ranked = await Article.objects.rank_by("body", "rust", mode="plain").all(conn)
Index declaration — PostgreSQL uses TSVector columns; other drivers index base
text columns via Meta.full_text_indexes:
from ferrum.models import Field, FullTextIndex
class Article(Model):
search_vector: Annotated[TSVector, Field(fts_config="english")] | None = None
body: str = ""
class Meta:
full_text_indexes = [FullTextIndex(fields=("body",), config="english")]
Query strings are always bound parameters; fts_config and index names come from
model-metadata allowlists only. See Getting Started → Vector and full-text columns
and API Reference for per-dialect DDL and operator mapping.
Architecture
┌──────────────────────────┐
│ Python API │
│ Models / QuerySets │
└────────────┬─────────────┘
│
▼
┌──────────────────────────┐
│ Ferrum Core │
│ (Rust Engine) │
├──────────────────────────┤
│ Query Compiler │
│ SQL AST │
│ Result Decoder │
│ Migration Planner │
└────────────┬─────────────┘
│
▼
┌──────────────────────────┐
│ PostgreSQL │
└──────────────────────────┘
Roadmap
v0.1 (complete)
- PostgreSQL support
- Basic CRUD operations
- Async query execution
- Pydantic models
- Query builder
- Type-safe filters
- Transactions and savepoints
- Bulk operations (
bulk_create,bulk_update,bulk_delete) - Migrations (schema diff, apply, revert, CLI)
- Relationships (ForeignKey, OneToOne, ManyToMany)
- pgvector KNN search and HNSW/IVFFLAT index DDL
- Full-text search (cross-dialect: PostgreSQL, MySQL, SQLite FTS5, SQL Server)
- Observability hooks (Tier A/B/C)
- CLI (
makemigrations,migrate,revert,showmigrations,inspectdb,resetdb)
v0.2 (in progress)
- Upsert API (
upsert,bulk_upsertwith conflict targets andRETURNING) - Composite primary keys
- Array field types (
uuid[],text[], scalar arrays) - JSONB operators (
__contains,__has_key) - RLS / tenant session helpers (
set_config,tenant_session) -
call_functionfor allowlisted stored-procedure calls - Migration ops for extensions, RLS policies, and function DDL
- pgvector similarity score projection (
vector_searchhelper) -
Field(db_default=..., nullable=...)— first-class DB-side defaults and nullability override -
makemigrationsautodiff for index / default / nullability changes on existing tables - Typed aggregates, filtered
UPDATE … RETURNING, and bounded QuerySet streaming - JSONB-list/generated/read-only metadata and read-only PostgreSQL schema drift reports
- Query optimization (deferred fields, prefetch tuning)
- Advanced relationship loading
v1.0
- Production-ready stability
- Performance benchmarking suite
- Full documentation site
Project Status
Ferrum is currently in active development.
The API is not yet stable and breaking changes should be expected until the first public release.
Installation
# PostgreSQL (most common)
pip install 'ferrum-orm[pg]'
# PostgreSQL + migrations CLI
pip install 'ferrum-orm[pg,cli]'
# MySQL
pip install 'ferrum-orm[mysql]'
# SQLite + migrations CLI (testing / local dev)
pip install 'ferrum-orm[sqlite,cli]'
# SQL Server (also needs a system ODBC driver, e.g. msodbcsql18)
pip install 'ferrum-orm[mssql]'
# Optional MessagePack wire format for the Python<->Rust boundary
pip install 'ferrum-orm[msgpack]'
# Everything (all drivers + CLI + dotenv)
pip install 'ferrum-orm[all]'
# Core ORM only (no database driver — install a driver extra before connecting)
pip install ferrum-orm
Bare ferrum-orm installs Pydantic and the Rust core only. Choose a driver extra
(pg, mysql, sqlite, or mssql) before calling ferrum.connect().
MySQL, SQLite, and SQL Server are thin-parity backends: they support core CRUD
and migrations but not transactions, upsert, bulk_update, RLS, or pgvector
(PostgreSQL only). SQL Server connects via aioodbc/pyodbc and requires a system
ODBC driver such as msodbcsql18; DSNs use the mssql:// or sqlserver:// scheme.
Wire format (advanced)
The Python↔Rust IR/hydration boundary defaults to JSON. Installing the msgpack
extra lets you switch it to MessagePack, selected via the FERRUM_WIRE_FORMAT
environment variable (json | msgpack) or the [ferrum] wire_format key in
ferrum.toml / pyproject.toml. JSON remains the default; MessagePack is opt-in.
From source, build the native extension with maturin develop (or mise run dev).
Examples
Runnable samples live under examples/:
examples/simple/— async CRUD script (no web framework)examples/migrations/— CLI, plan generation, apply, and forward fix-upsexamples/fastapi_quickstart/— FastAPI integration
Contributing
Contributions are welcome. Start with CONTRIBUTING.md for local setup,
scoped verification, architecture rules, and pull request expectations.
License
Apache License 2.0
Metadata
Release files for ferrum-orm 0.1.14
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ferrum_orm-0.1.14.tar.gz | 196.3 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| ferrum_orm-0.1.14-cp311-abi3-manylinux_2_28_x86_64.whl | CPython 3.11 | abi3 | Linux glibc 2.28+ x86-64 | Details |
| ferrum_orm-0.1.14-cp311-abi3-manylinux_2_28_aarch64.whl | CPython 3.11 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| ferrum_orm-0.1.14-cp311-abi3-macosx_11_0_arm64.whl | CPython 3.11 | abi3 | macOS 11.0+ ARM64 | Details |
| ferrum_orm-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl | CPython 3.11 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 3.7 MB
Release files / ferrum_orm-0.1.14.tar.gz
| Download URL | ferrum_orm-0.1.14.tar.gz |
|---|---|
| Size | 196.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7c5d18cd6b9f2d92a7839c1101b862abac37779fe41a7b79dd61f0b5aa9bdf8c
|
|
BLAKE2b-256 checksum How to use checksums |
c7ea100c5452d51fe2bb563cea094d0d28f770a7a53969ee8e001bbbde422c2c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / ferrum_orm-0.1.14-cp311-abi3-manylinux_2_28_x86_64.whl
| Download URL | ferrum_orm-0.1.14-cp311-abi3-manylinux_2_28_x86_64.whl |
|---|---|
| Size | 917.3 kB |
| Tags | CPython 3.11 Linux glibc 2.28+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
f3eb9cd60fd3b9ec8cc3415fa5aaa8b45d4efe505be803d94335465ba02ff24c
|
|
BLAKE2b-256 checksum How to use checksums |
06215e41db56cd6fe01d64fb822892e76f1ba55e60d610f7d3923e268a23e001
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / ferrum_orm-0.1.14-cp311-abi3-manylinux_2_28_aarch64.whl
| Download URL | ferrum_orm-0.1.14-cp311-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 892.1 kB |
| Tags | CPython 3.11 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
faf6c28ce9c8cf15a8809eefde3e2139bcf827f33d173ade294aed14cd630cfd
|
|
BLAKE2b-256 checksum How to use checksums |
18b3eb90e40aecda81ceaefa7d6ac0d207d5d7dd538aa19da9029b8fbfefef77
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / ferrum_orm-0.1.14-cp311-abi3-macosx_11_0_arm64.whl
| Download URL | ferrum_orm-0.1.14-cp311-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 838.3 kB |
| Tags | CPython 3.11 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
541b531cab67d93cbf96c592e3ff554091f73adec79663eca8e57794c3fdf950
|
|
BLAKE2b-256 checksum How to use checksums |
6043b5e1a949927e54fd5c05c4a71131ef8b04b0e40260e6be3d5a3697c73882
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / ferrum_orm-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl
| Download URL | ferrum_orm-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 877.4 kB |
| Tags | CPython 3.11 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
e1ad0337ec2cdb585aac7e80027edfa142ab82f44109c4e891942ac61251d137
|
|
BLAKE2b-256 checksum How to use checksums |
0a241f2de7b33ec2f4c367a282aa7b29612a4b4e14ba85e52dadaa559c827da6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency log