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.0rc5.tar.gz (73.4 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.0rc5-py3-none-any.whl (70.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: half_orm-1.0.0rc5.tar.gz
  • Upload date:
  • Size: 73.4 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.0rc5.tar.gz
Algorithm Hash digest
SHA256 1ce6a92b7c22097fa2cec7ec8ab18192ba4549d6f5aa6bb7eb375e3d699a9888
MD5 64226af9b372975ab1e561218d3494c7
BLAKE2b-256 ccd128f91c702d90bc4263618e6b65681c4e0b1cd27321128d6a432f26e94952

See more details on using hashes here.

File details

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

File metadata

  • Download URL: half_orm-1.0.0rc5-py3-none-any.whl
  • Upload date:
  • Size: 70.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.0rc5-py3-none-any.whl
Algorithm Hash digest
SHA256 a29ad278bc007ba0b4c22dc30d4f6b68119881684db25dd6b52dfa0461635f3c
MD5 3f0fe78df3f726649e94547394bee941
BLAKE2b-256 26016abd9baa317231fc16821bd25167b0c1fcc7c4ba5ffe50827f0117bab04f

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