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.0rc11.tar.gz (75.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.0rc11-py3-none-any.whl (72.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: half_orm-1.0.0rc11.tar.gz
  • Upload date:
  • Size: 75.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.0rc11.tar.gz
Algorithm Hash digest
SHA256 4f82dc378cd0f2122697eddbe2fed502335a9fae003fc11e5c566c2764b1f9c1
MD5 cebde5ed4f1d6a9b93180dd959e20287
BLAKE2b-256 0492545158b97bdf355d923772741cc3986044fa2afb92867bdd00834e179a39

See more details on using hashes here.

File details

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

File metadata

  • Download URL: half_orm-1.0.0rc11-py3-none-any.whl
  • Upload date:
  • Size: 72.5 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.0rc11-py3-none-any.whl
Algorithm Hash digest
SHA256 83aa13e5410b44bf52fd0a2afb1ec9076c0e19ca300c3be08b2b00319cdf4a01
MD5 75d22540c8bb0440b6ac84fb07dd028a
BLAKE2b-256 e5bccf59babcd32b93056e8c899001abc074cff397da78acd720d7c778187cfd

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