Skip to main content

MetaORM

Async repository layer over SQLModel. Define a table, a repository with keyword arguments, and you have a complete async CRUD layer.

📖 Full documentation

  • Minimal APIcreate_item, get_items, update_items, delete_items. That's it.
  • Built-in DTO mapping — return table instances directly or map to separate Pydantic models.
  • Intuitive transactions — every CRUD call runs in a transaction; explicit transaction() context manager for custom scopes.
  • Nested transactions (savepoints)nested_transaction() allows partial rollback inside a shared transaction.
  • Multi-repo atomic transactionsRepositoriesContainer lets several repositories share one atomic transaction.
  • Filters, pagination, sorting — powered by pydantic-filters.
  • Eager loading — pass SQLAlchemy joinedload / selectinload via options.

Install

pip install metaorm

Requires Python >=3.12.

Note: metaorm depends on a patched version of pydantic-filters that is not yet available on PyPI. The dependency is pulled automatically from GitHub during installation.

Quick start

from metaorm import BaseFilter, BaseRepository, BaseTable, RepositorySettings, Field


class UserTable(BaseTable, table=True):
    __tablename__ = "users"
    id: int | None = Field(default=None, primary_key=True)
    name: str
    email: str = Field(unique=True)


class UserFilter(BaseFilter):
    name: str | None = None
    email: str | None = None


class UserRepository(BaseRepository, table=UserTable, filter_=UserFilter):
    pass


async def main():
    repo = UserRepository(
        settings=RepositorySettings(dsn="sqlite+aiosqlite:///:memory:"),
    )
    await repo.create_tables()

    user = await repo.create_item(UserTable(name="Alice", email="alice@example.com"))
    print(user.id, user.name)

    all_users = [u async for u in repo.get_items()]
    print(len(all_users))

Repository API

Subclass BaseRepository with keyword arguments table, filter_, and optionally dto:

class MyRepository(BaseRepository, table=MyTable, filter_=MyFilter):
    pass  # returns table instances directly


class MyRepositoryWithDto(BaseRepository, table=MyTable, filter_=MyFilter, dto=MyDto):
    pass  # maps rows to MyDto

Keyword arguments are checked at class-definition time. If you forget table or filter_, Python raises TypeError immediately. table= must still be provided on the first subclass in the hierarchy.

Constructor

# Simple — container is created internally
repo = MyRepository(settings=RepositorySettings(dsn="..."))

# Advanced — share a container for atomic multi-repo transactions
container = RepositoriesContainer(settings=settings)
repo = MyRepository(container=container)

Methods

Method Signature Description
create_tables async () -> None Creates the table in the database.
create_item async (item) -> Any Inserts one row. Returns the table instance or DTO when dto= is set.
get_items async (filter_=None, pagination=None, sort=None, options=None) -> AsyncGenerator[Any] Streams matching rows. options accepts SQLAlchemy eager-loading strategies such as joinedload.
get_items_count async (filter_=None) -> int Returns the number of matching rows.
update_items async (filter_=None, options=None, **values) -> AsyncGenerator[Any] Updates matching rows and yields the updated instances.
delete_items async (filter_=None) -> None Deletes matching rows.
transaction async contextmanager () -> AsyncSession Explicit transaction scope. Automatically used by all CRUD methods. Reuses an existing session when nested.
nested_transaction async contextmanager () -> AsyncSession Creates a savepoint (nested transaction). Rolls back only the inner scope on error while leaving the outer transaction intact.

Multi-repository transactions

Use RepositoriesContainer when you need a single atomic transaction spanning multiple repositories:

from metaorm import RepositoriesContainer

container = RepositoriesContainer(settings=settings)
user_repo = container.get_repository(UserRepository)
order_repo = container.get_repository(OrderRepository)

async with container.transaction():
    user = await user_repo.create_item(UserTable(name="Alice"))
    await order_repo.create_item(OrderTable(user_id=user.id, total=100))

container.transaction() stores the session in a contextvars.ContextVar. All repository operations within the async with block automatically reuse that session. Nested transaction() calls yield the same session.

For partial rollback inside a shared transaction use container.nested_transaction() (or repository.nested_transaction()). It creates a SQLAlchemy savepoint: an error inside the block rolls back only the savepoint, leaving the outer transaction open for further operations or commit.

More examples

See examples/ for detailed usage patterns:

Exceptions

DatabaseException
├── NotFoundError
├── HaveNoSessionError
└── AlreadyExistsError

All repository methods raise DatabaseException subclasses or SQLAlchemy errors.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

metaorm-0.2.0.tar.gz (12.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

metaorm-0.2.0-py3-none-any.whl (7.7 kB view details)

Uploaded Python 3

File details

Details for the file metaorm-0.2.0.tar.gz.

File metadata

  • Download URL: metaorm-0.2.0.tar.gz
  • Upload date:
  • Size: 12.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"42","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for metaorm-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b76b0db491498c0328cdae1b752f87fdc83db76f1150c4e4f1ccba99005c9862
MD5 c4ba73aa9bcf99ada2679f7eb9cd8a94
BLAKE2b-256 91210133a834926acd44218f1c370fb8e45f7be413fc53b0beffcdcb9d179865

See more details on using hashes here.

File details

Details for the file metaorm-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: metaorm-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 7.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Fedora Linux","version":"42","id":"","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for metaorm-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 09744359f2c6e96db1ce89fea6ba964bf83063f4221afb88cd9f665f645e12d6
MD5 70dc53b476f321dad7bcaa43c7a1fa8d
BLAKE2b-256 ff1740743bad5db407e1224cfe23350ab3a67dcc61ef8ec89aff7b35db276c99

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page