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.0rc12.tar.gz (76.2 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.0rc12-py3-none-any.whl (72.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: half_orm-1.0.0rc12.tar.gz
  • Upload date:
  • Size: 76.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.19

File hashes

Hashes for half_orm-1.0.0rc12.tar.gz
Algorithm Hash digest
SHA256 00e98498eb335c595ced5da95c69799209cbdf5998673b0cea2a3c9f8e5215e3
MD5 1f6bfc8fa1e20d0cb6304f0eed2f9020
BLAKE2b-256 6ced043fffb715745c33eb385c8fd99b4ceee12fb77d0d747b9d80e95b35593e

See more details on using hashes here.

File details

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

File metadata

  • Download URL: half_orm-1.0.0rc12-py3-none-any.whl
  • Upload date:
  • Size: 72.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.8.19

File hashes

Hashes for half_orm-1.0.0rc12-py3-none-any.whl
Algorithm Hash digest
SHA256 525e3d52d615b51354caee19fd65e25e264745603f5cd87e09fa66faaa90b4e2
MD5 c7f3f6b424c36b80e6d9219f42b880f9
BLAKE2b-256 16f36fec1c1909fbe4caf8871b7c2145d9d01b179d4e3c460fcafab369d9530f

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