Modern Python ORM with async support, full type annotations and a generator-expression query DSL.
Features
- Type-annotated fields —
PK[int],Req[str],Opt[str],Set[T],Single[T] - Auto-save sessions — create entities inside
db_sessionand they are committed automatically - PonyORM-compatible DSL — generator-expression queries,
Entity[pk],Entity.get(), lifecycle hooks - Full async support —
AsyncDatabase,await db.aselect(...),Entity.aselect(),Entity.aget() - Built-in migrations CLI —
nextorm makemigrations/nextorm migrate - Three providers — SQLite, PostgreSQL (psycopg3), MariaDB
- 100% branch coverage enforced in CI
Installation
pip install nextorm[sqlite] # SQLite (aiosqlite)
pip install nextorm[postgres] # PostgreSQL (psycopg3)
pip install nextorm[mariadb] # MariaDB (asyncmy + PyMySQL)
pip install "nextorm[sqlite,postgres,mariadb]" # all drivers
Quick start
from nextorm import Database, Entity, PK, Req, Opt, Set, Single, db_session
# Define entities — no database coupling required
class Tag(Entity):
name: Req[str]
products: Set["Product"] # many-to-many back-reference
class Product(Entity):
name: Req[str] = Req(64) # positional shorthand: max_len=64
price: Req[float]
sku: Req[str] = Req(column="product_sku", unique=True) # marker-call options
tags: Set[Tag] # many-to-many
summary: Opt[str] # Opt[str]/[LongStr] use empty string for None by default
# Create and connect the database
db = Database(entities=[Tag, Product]) # entities can also be auto-discovered
db.bind("sqlite", ":memory:")
db.generate_mapping(create_tables=True)
# Write — entities are tracked and committed automatically
with db_session:
t = Tag(name="sale")
p = Product(name="Widget", price=9.99, sku="WGT-1")
p.tags.add(t)
# ← INSERT fires here; p.id and t.id are now set
# Read — class-level shortcuts (no explicit db reference needed)
widgets = Product.select().filter(Product.price < 20).fetch_all()
widget = Product.get(name="Widget") # None if not found
widget = Product[1] # KeyError if not found
Async quick start
import asyncio
from nextorm import AsyncDatabase, Entity, PK, Req, db_session
class Task(Entity):
title: Req[str]
done: Req[bool]
async def main() -> None:
db = AsyncDatabase(entities=[Task])
await db.bind("sqlite", ":memory:")
await db.generate_mapping(create_tables=True)
async with db_session:
Task(title="Buy milk", done=False)
pending = await Task.aselect().filter(Task.done == False).fetch_all()
task = await Task.aget(title="Buy milk") # None if not found
print(pending)
asyncio.run(main())
Migrations
nextorm makemigrations # generate a migration from model changes
nextorm migrate # apply pending migrations
nextorm showmigrations # list migration history
Migrating from PonyORM
NextORM's API is intentionally close to PonyORM's. The main differences:
| PonyORM | NextORM |
|---|---|
class Product(db.Entity) |
class Product(Entity) |
Required(str) |
Req[str] |
Optional(str) |
Opt[str] |
PrimaryKey(int, auto=True) |
PK[int] |
Required(Order) (FK side) |
Single[Order] |
Set("Line") (back-reference) |
Set["Line"] |
select(p for p in Product if ...) |
identical |
Product[42] |
identical |
Product.get(name="x") |
identical |
See the migration guide for details.
Documentation
Full docs at nextorm.readthedocs.io.
Community
Questions, ideas, and feedback are welcome in the NextORM Discord community.
- 💬 Get help with models, queries, relations, and async usage
- 🧪 Share bug reports with minimal reproductions
- 🧭 Discuss roadmap ideas and RFCs
- 📝 Suggest docs improvements
Join here: https://discord.gg/PevUtddqCM
Development
pdm install
pdm test # run tests
pdm coverage # tests + branch coverage (must be 100%)
pdm typecheck # pyright + mypy
pdm lint # ruff
pdm format # ruff format
pdm docs-html # build Sphinx docs
Acknowledgements
NextORM's API design and query DSL are heavily inspired by PonyORM, created by Alexander Kozlovsky, Alexey Malashkevich and Alexander Tischenko, released under the Apache License 2.0.
NextORM is a new, independent implementation and shares no source code with PonyORM.
Release files for nextorm 0.4.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nextorm-0.4.3.tar.gz | 322.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nextorm-0.4.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 495.3 kB
Release files / nextorm-0.4.3.tar.gz
| Download URL | nextorm-0.4.3.tar.gz |
|---|---|
| Size | 322.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9c1977c2a61178f549fce0402af851eb11fea565cedc91be605415d06bf7a7e8
|
|
BLAKE2b-256 checksum How to use checksums |
10793770527258b292a91f05ef9a95547a06a34b48416d55053b0eca49496fa5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
pdm/2.28.2 CPython/3.13.15 Linux/6.17.0-1022-azure
|
Release files / nextorm-0.4.3-py3-none-any.whl
| Download URL | nextorm-0.4.3-py3-none-any.whl |
|---|---|
| Size | 172.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
15bc8c70e614258fab6f40dad6412861223eb829d055c1e15180ac146fdcdd0e
|
|
BLAKE2b-256 checksum How to use checksums |
7dc84a9bff1ea991317a78e54497ea879519aed2c9a3187d8592d454e0f51645
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
pdm/2.28.2 CPython/3.13.15 Linux/6.17.0-1022-azure
|