Skip to main content

A database-first ORM for PostgreSQL

Project description

halfORM

PyPI version Python versions PostgreSQL versions License Tests Coverage Downloads

⚠️ BREAKING CHANGES in v1.0.0

ho_get() is now public and returns a dict directly. It raises NotFoundError (0 rows) or MultipleRowsError (> 1 row) instead of counting first.

Deprecated query-builder setters removedho_limit, ho_offset, ho_order_by, ho_distinct no longer exist as setters. Pass these as keyword arguments to ho_select():

# Before (0.x)
rel.ho_limit = 10
rel.ho_order_by = 'name'
list(rel)

# After (1.0)
list(rel.ho_select(limit=10, order_by='name'))

FKEYS_PROPERTIES / FKEYS class attributes removed — use Fkeys only.

ho_cast() now raises CastError if the target is not in the PostgreSQL inheritance hierarchy of the relation.

See CHANGELOG.md for the full list of changes.

halfORM is a database-first ORM for PostgreSQL. Your schema lives in the database; halfORM introspects it at runtime and gives you Python objects to work with your data. No migrations, no code generation.

The central idea: a Relation object is a predicate. It describes the logical condition that rows must satisfy to belong to the relation. Its extension — the set of rows currently satisfying the predicate in the database — is what you read, update, or delete.

Key features

  • Database-first — the schema lives in PostgreSQL, not in Python classes.
  • Predicate model — relations are predicates; set operators (|, &, -) compose them before any SQL is sent.
  • FK navigation — join across tables (and views) by setting FK attributes; explicit Fkeys dict for views where PostgreSQL stores no FK metadata.
  • Sync + async — every executor has an a-prefixed async counterpart (ho_aselect, ho_ainsert, …).
  • Bulk loadho_copy / ho_acopy for high-throughput inserts via PostgreSQL COPY.
  • No magic — queries are built from the predicate you describe and executed only when you call an executor. ho_mogrify() shows the exact SQL.

Install

pip install half_orm

Requires psycopg 3 (psycopg[binary]).

Configure

# ~/.half_orm/blog
[database]
name     = blog
user     = alice
password = secret
host     = localhost

Usage

from half_orm.model import Model
from half_orm.relation_errors import NotFoundError, MultipleRowsError

blog   = Model('blog')
Post   = blog.get_relation_class('blog.post')
Author = blog.get_relation_class('blog.author')

# Insert — returns the inserted row as a dict
alice = Author(
    first_name='Alice', last_name='Martin', email='alice@example.com'
).ho_insert()

# Query — Author(last_name='Martin') is a predicate, not a query
for row in Author(last_name='Martin').ho_select('id', 'email'):
    print(row)

# Get exactly one row
try:
    author = Author(last_name='Martin').ho_get()
except NotFoundError:
    print("no such author")
except MultipleRowsError:
    print("ambiguous — more than one author named Martin")

# FK navigation — no JOIN written by hand
post = Post(id=1)
post.author_fk.set()                         # join all authors
for row in post.ho_select(json_agg={'author_fk': ['first_name', 'last_name']}):
    print(row['author_fk'])                  # {'first_name': 'Alice', 'last_name': 'Martin'}

# Set operators — compose predicates before hitting the database
recent   = Post(created_at=('>', '2024-01-01'))
featured = Post(featured=True)
combined = recent | featured                 # UNION
print(combined.ho_count())

# Update / delete
Author(id=alice['id']).ho_update(email='alice@newdomain.com')
Author(id=alice['id']).ho_delete()

Documentation

Extensions

Extension Description
half-orm-dev Development tools — half_orm dev project scaffolding, patch management, and schema synchronisation.

License

halfORM is licensed under the GPL-3.0 license.

Project details


Release history Release notifications | RSS feed

Download files

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

Source Distribution

half_orm-1.0.0rc14.tar.gz (76.9 kB view details)

Uploaded Source

Built Distribution

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

half_orm-1.0.0rc14-py3-none-any.whl (73.1 kB view details)

Uploaded Python 3

File details

Details for the file half_orm-1.0.0rc14.tar.gz.

File metadata

  • Download URL: half_orm-1.0.0rc14.tar.gz
  • Upload date:
  • Size: 76.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for half_orm-1.0.0rc14.tar.gz
Algorithm Hash digest
SHA256 50411d450558ff303444a4bb8b81a7a1b28ff850cddf7319c762f4301eb68346
MD5 e468eb3cb9b2e8e98b412f26f11a299a
BLAKE2b-256 4b2aca401eb9c16c63426354751ff2c6b1d3a010242f0a93bae877e136245d1b

See more details on using hashes here.

File details

Details for the file half_orm-1.0.0rc14-py3-none-any.whl.

File metadata

  • Download URL: half_orm-1.0.0rc14-py3-none-any.whl
  • Upload date:
  • Size: 73.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for half_orm-1.0.0rc14-py3-none-any.whl
Algorithm Hash digest
SHA256 a162bd3969c117007d74a76195b3f049fd1267bcccb6354b5399cec8152ad221
MD5 46f0a7a6b9b799785f0aa78125d2db8b
BLAKE2b-256 66dde78a4732d807c409900dd730cc36f73adb64bcfd3253b3d95c38c70f6b54

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