lzdb
A schema-emergent database layer for Python and PostgreSQL.
Overview
lzdb (Lazy Database) is a Python persistence layer that allows database structure to emerge from the objects being stored.
Instead of designing tables, foreign keys, and migrations up front, lzdb automatically creates collections, evolves schemas, manages foreign keys, and persists relationships as your application grows.
The guiding principle is:
Store first. Model later.
lzdb is particularly useful for:
- Research projects
- Scientific data processing
- Exploratory analytics
- Rapid prototyping
- ETL staging systems
- Knowledge graph applications
- Evolving business domains
Main Concepts
Collections
Objects with the same structure belong to the same collection.
Example:
m1 = lzitem(
param="2004",
starttime="03-jan-2000:00:00:00",
endtime="04-jan-2000:00:00:00"
)
m2 = lzitem(
param="2005",
starttime="03-jan-2000:00:00:00",
endtime="04-jan-2000:00:00:00"
)
Both objects belong to the same collection because they share the same schema.
Collection Signatures
lzdb records a collection signature such as:
endtime,param,starttime
The signature determines collection membership.
It does not enforce uniqueness.
Duplicate Rows
Duplicates are allowed.
a = lzitem(name="sat1")
b = lzitem(name="sat1")
Both rows are stored independently.
Object identity is provided by PostgreSQL-generated IDs.
Installation
Install from PyPI
pip install lzdb2
Build Locally
python -m build
Install:
pip install dist/*.whl
Getting Started
import psycopg as pg
from lzdb import *
conn = pg.connect(
dbname="test",
host="localhost"
)
dbms = LZDB(conn)
Enable tracing:
dbms = LZDB(conn, traceon=True)
Convenience Helpers
Expose shortcuts in the current namespace:
dbms.expose()
Creates:
lzitem
lzitems
lzc
lzcnames
dd
pp
Example:
sat = lzitem(name="sat1")
Creating Objects
sat = dbms.newItem(
name="SAT1"
)
or:
sat = lzitem(name="SAT1")
Data is not persisted until:
dbms.commit()
ensure()
Retrieve an existing matching object or create one.
sat = dbms.ensure(name="SAT1")
Behavior:
0 matches -> create
1 match -> return object
N matches -> return oldest object
This makes object retrieval deterministic.
Foreign Keys
References to lzdb objects automatically become PostgreSQL foreign keys.
sat = lzitem(name="SAT1")
measurement = lzitem(
satellite=sat,
timestamp="2025-01-01"
)
lzdb automatically creates the FK column and constraint.
After reload:
measurement["satellite"]
returns the referenced object.
Automatic Schema Evolution
Objects can gain fields after creation.
sat["operator"] = "ESA"
sat["launch_year"] = 1998
During commit, lzdb automatically updates the database schema.
No migrations are required.
Links
Foreign keys represent structural relationships.
Links represent semantic relationships.
Single Link
sat.link(measurement)
Multiple Links
sat.link([
measurement1,
measurement2,
measurement3
])
Links are stored inside the system table:
lzdb_links
Relationship Types
LZDB_REL_DIRECTED
LZDB_REL_UNDIRECTED
Example:
sat.link(
measurement,
LZDB_REL_UNDIRECTED
)
Retrieving Links
for item in dbms.linkedItems(sat):
print(item)
Querying
Retrieve Everything
items = dbms.items()
Filter by Attributes
items = dbms.items(
param="2004"
)
Filter by Collection
items = dbms.items(
collection=mycollection
)
Realistic Workflow Example
from lzdb import *
measurements = [
lzitem(param="2004"),
lzitem(param="2005"),
lzitem(param="2006")
]
sat = lzitem(name="sat1")
sat.link(measurements)
sat["operator"] = "ESA"
sat["launch_year"] = 1998
dbms.commit()
for item in dbms.linkedItems(sat):
print(item)
lzdict
lzdict provides lazy parquet loading.
dd = lzdict()
data = dd["PQTFILE"]
Persistence Model
Objects exist in one of three states:
- New
- Loaded
- Dirty
Dirty objects are automatically updated during commit.
Current Dogma
- Collections emerge from structure.
- IDs define identity.
- Duplicate rows are allowed.
- References become foreign keys.
- Semantic relationships belong in lzdb_links.
- Schemas evolve automatically.
- Nothing is persisted until commit().
- Existing objects may continue to evolve.
Testing
lzdb includes regression tests covering:
- collection creation
- collection reuse
- duplicate rows
- ensure()
- foreign keys
- foreign-key reload
- graph links
- undirected links
- persistence
- schema evolution
Run:
pytest -v
Manifesto
lzdb is more than a persistence layer. It is an experiment in Lazy Data Modeling.
Traditional data modeling assumes that the structure of the data must be understood before the data can be stored.
lzdb challenges that assumption.
Instead of designing schemas, foreign keys, constraints, and migrations upfront, lzdb encourages:
- storing first
- observing actual usage
- allowing collections to emerge naturally
- evolving structure incrementally
- separating structural relationships from semantic relationships
For the complete rationale and philosophy behind the project, see:
The manifesto discusses:
- why schemas should emerge from data
- why duplication is not always the enemy
- collection signatures
- structural versus semantic relationships
- exploratory database design
- the principles of Lazy Data Modeling
License
GNU General Public License v3.0 or later (GPL-3.0-or-later).
See the LICENSE file included with the project.
Metadata
Release files for lzdb2 2.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| lzdb2-2.0.1.tar.gz | 27.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| lzdb2-2.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.5 kB
Release files / lzdb2-2.0.1.tar.gz
| Download URL | lzdb2-2.0.1.tar.gz |
|---|---|
| Size | 27.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
198c1a018399496ff516aeea68e3bcbeca833e268393112e0bdda5712dd319f3
|
|
BLAKE2b-256 checksum How to use checksums |
e754adcc978b232d0be9e7cee8eb2e2bec3ce13af50c7275cfe37298230a30bd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / lzdb2-2.0.1-py3-none-any.whl
| Download URL | lzdb2-2.0.1-py3-none-any.whl |
|---|---|
| Size | 22.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d9c3785e95ba7e8c5af10bc322b85c9b2346cc490ab86b35db177b31bd1e2fe1
|
|
BLAKE2b-256 checksum How to use checksums |
dbfaf2537db8d3091cd0c87452670b042aa6eaa7c80e06fb0b23842f7011b171
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|