Skip to main content

Object-relational mapping library for PostgreSQL

Project description

orm1

orm1 is a minimal asynchronous Object-Relational Mapping (ORM) library for Python that makes it easy to map your Python classes to PostgreSQL database tables. Built on top of asyncpg, orm1 provides a minimal yet powerful API for CRUD operations, complex query building, and relationship management – all with full async support.


Features

  • Asynchronous by Design
    Leverage Python’s async/await syntax with an asyncpg backend for high-performance, non-blocking database operations.

  • Auto-mapping with Type Hints
    Automatically generate entity mappings from your class type hints. orm1 can infer column names and even detect relationships (both singular and plural) based on your annotations.

  • Aggregate Support (Parent-Child Relationship)
    Define aggregates naturally by specifying child entities that are part of an aggregate. For instance, a Post aggregate can include multiple PostAttachment instances.

  • Fluent Query Builder
    Construct complex SQL queries using a Pythonic API. orm1 supports filtering, joins, ordering, and pagination, all while keeping your code readable.

  • Raw SQL Queries
    When you need full control, execute raw SQL queries with safe parameter binding.

  • Flexible Transaction Management
    Manage database transactions with simple begin(), commit(), and rollback() methods.


Installation

Install orm1 from PyPI with pip:

pip install orm1

Getting Started

Defining Your Entities

Define your entity classes with type hints to describe fields and relationships. Use the auto.mapped decorator from the AutoMappingBuilder to register your classes with orm1. Column names are automatically generated (using snake_case conversion) unless overridden in the configuration.

Below is an example using a Post aggregate with its child PostAttachment:

from orm1 import auto

@auto.mapped(
    table="posts",
    primary_key="id"
)
class Post:
    id: int
    title: str
    content: str
    # Define the aggregate relationship: a post has many attachments.
    attachments: list["PostAttachment"]

@auto.mapped(
    table="post_attachments",
    primary_key="id",
    parental_key="post_id"  # Indicates this entity belongs to a Post aggregate.
)
class PostAttachment:
    id: int
    post_id: int
    file_name: str
    url: str

The mappings above assume that you have a PostgreSQL database with tables posts and post_attachments. The PostAttachment table has a foreign key post_id that references the id column of the posts table.

CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    title TEXT NOT NULL,
    content TEXT NOT NULL
);

CREATE TABLE post_attachments (
    id SERIAL PRIMARY KEY,
    post_id INT NOT NULL REFERENCES posts(id),
    file_name TEXT NOT NULL,
    url TEXT NOT NULL
);

Building Mappings

After decorating your entity classes, build the mappings. This process collects the configuration and type hints to create the internal mapping definitions used by orm1.

from orm1 import auto

mappings = auto.build()

Creating a Session

Create a session to interact with your database. The session uses a backend (such as AsyncPGSessionBackend) for executing SQL statements.

import asyncpg
from orm1 import Session, AsyncPGSessionBackend

async def main():
    # Create an asyncpg connection pool.
    pool = await asyncpg.create_pool(dsn="postgresql://user:password@localhost/dbname")
    
    # Initialize the backend and session.
    backend = AsyncPGSessionBackend(pool)
    session = Session(backend, mappings)
    
    # Now you can perform CRUD operations with your session.

CRUD Operations

Inserting and Saving Entities

To insert a new entity or update an existing one, simply call save on your session. orm1 will determine whether to insert or update based on whether the entity is already tracked.

# Create a new post with an attachment.
post = Post()
post.title = "Introducing orm1"
post.content = "orm1 is a lightweight asynchronous ORM for Python."

attachment = PostAttachment()
attachment.file_name = "diagram.png"
attachment.url = "http://example.com/diagram.png"

post.attachments = [attachment]

# Save the post (and cascade save the attachment as part of the aggregate).
await session.save(post)

Querying Entities

Use the query builder to fetch entities with filtering, joins, and ordering.

# Query for a post by title.
query = session.query(Post, alias="p")
query = query.filter('p."title" = :title', title="Introducing orm1")
posts = await query.fetch()

# Get a single post.
post = await query.fetch_one()

Updating Entities

Modify the attributes of an entity and call save again. orm1 will automatically update the corresponding database record.

# Update the post's title.
post.title = "Introducing orm1 - A Lightweight Async ORM"
await session.save(post)

Deleting Entities

Remove an entity by calling delete on the session. Deleting an aggregate root will cascade and remove its child entities as needed.

await session.delete(post)

Advanced Querying

Joining Related Entities

Join related tables by using the join or left_join methods on a query. This is useful when you need to filter or retrieve data based on relationships.

# Join post attachments with their parent posts.
query = session.query(PostAttachment, "a")
query = query.join(Post, "p", 'a.post_id = p.id')
query = query.filter('p.title = :title', title="Introducing orm1")
attachments = await query.fetch()

Ordering and Pagination

Sort your query results and paginate them easily.

# Ordering: specify ascending or descending order.
query = session.query(PostAttachment, "a")
query = query.order_by(query.asc('a.file_name'))
attachments = await query.fetch(limit=10, offset=0)

# Pagination: use paginate to get page information.
page = await query.paginate(first=10)
print("Attachment IDs on current page:", page.ids)
print("Has next page?", page.has_next_page)

Executing Raw SQL Queries

If you need more control, execute raw SQL statements while still benefiting from parameter binding.

raw_query = session.raw("SELECT * FROM post_attachments WHERE file_name LIKE :pattern", pattern="%diagram%")
results = await raw_query.fetch()

API Reference

Session

  • get(entity_type, id)
    Retrieve a single entity by its primary key.

  • save(entity)
    Insert or update an entity (and its children, if applicable).

  • delete(entity)
    Delete an entity (and cascade delete its children).

  • query(entity_type, alias)
    Start building a query for the given entity type.

  • raw(query, **params)
    Create a raw SQL query.

  • Transaction Management:

    • begin(): Start a transaction.
    • commit(): Commit the current transaction.
    • rollback(): Roll back the current transaction.

SessionEntityQuery

  • filter(condition, **params)
    Add a filtering condition to the query.

  • join(target, alias, on, **params) / left_join(target, alias, on, **params)
    Join another table (or entity) into the query.

  • order_by(...)
    Specify ordering for the query results.

  • fetch(limit, offset)
    Execute the query and retrieve a list of entities.

  • fetch_one()
    Retrieve a single entity.

  • count()
    Count the number of matching entities.

  • paginate(first, after, last, before, offset)
    Paginate the query results.

AutoMappingBuilder

  • @auto.mapped(**kwargs)
    Decorator to register and configure an entity’s mapping. Configuration options include specifying the table name, primary key, parental key, field configurations, and child (relationship) configurations.

  • auto.build()
    Build and return all entity mappings based on the registered configurations.


License

This project is licensed under the Apache License.

Project details


Download files

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

Source Distribution

orm1-0.1.0.tar.gz (22.0 kB view details)

Uploaded Source

Built Distribution

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

orm1-0.1.0-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

File details

Details for the file orm1-0.1.0.tar.gz.

File metadata

  • Download URL: orm1-0.1.0.tar.gz
  • Upload date:
  • Size: 22.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.1

File hashes

Hashes for orm1-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b071dbe6f3b8371921343eddd1cf0e6e188e2d4f1a6bc6edbae72fa9d4bea8cf
MD5 05b0283892443ba794abdf1954cf8323
BLAKE2b-256 11dc6e593655f99afb3024cbe3f7d9965c1de3bc5e3f15d6dc69f30daef94624

See more details on using hashes here.

File details

Details for the file orm1-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: orm1-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.1

File hashes

Hashes for orm1-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7f969fc98b39637aa328a3715f670d328170a2f3c39658e9fe0388dab15b9ca1
MD5 7684b4f45cf3d820a3484a4ab55f6275
BLAKE2b-256 4a211f1f350c917d52c2118899b65c6c2a58a4bc1f3fa4c6b03a9baf3e17e870

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page