Skip to main content

Semantic data access for SQL — map ontology-shaped models onto real schemas with explicit OntoMapper bindings (import ontosql)

Project description

OntoSQL

Semantic data access for SQL — map ontology-shaped models onto real database schemas and write CRUD in Python, not RDF.

Real databases are not one table per ontology class. OntoSQL separates physical SQLModel tables from semantic Pydantic entities and connects them with an explicit mapper. Application code uses semantic types; OntoSQL compiles SQL on the backend. RDF export uses TripleModel; graph-native apps can pair OntoSQL with SparqlModel — see Ecosystem.

Requirements: Python 3.10+. See Compatibility.

Install

pip install ontosql
pip install "ontosql[fastapi]"   # OntoRouter + content negotiation
pip install "ontosql[jsonld]"    # optional JSON-LD compact/frame (PyLD)
pip install "ontosql[sparql]"    # SparqlModel graph sync adapter
pip install "ontosql[shacl]"     # SHACL shape generation + validation

For async SQLite examples: pip install aiosqlite greenlet.

Start here

  1. Quick start (below) — models, maps, session CRUD in ~10 minutes
  2. Architecture — why two model layers and explicit maps
  3. Hybrid deployments — SQL + RDF graph sync (optional)
  4. Technical specification — full API reference
  5. FAQ · Troubleshooting

Runnable examples (after pip install ontosql):

python examples/person_org_demo.py          # sync CRUD
python examples/person_org_async.py         # async session (needs aiosqlite)
python examples/hybrid_person_org.py        # graph sync + import + SHACL
pip install "ontosql[fastapi]" uvicorn && python examples/person_org_api.py

Quick start

0. Database engine

from sqlmodel import Session, SQLModel, create_engine

from ontosql import OntoSession

engine = create_engine("sqlite:///./app.db")
SQLModel.metadata.create_all(engine)  # use Alembic in production

# Optional: seed data
with Session(engine) as raw:
  ...

1. Physical models (database truth)

from sqlmodel import Field, SQLModel


class OrgRow(SQLModel, table=True):
    __tablename__ = "orgs"
    id: int | None = Field(default=None, primary_key=True)
    name: str


class PersonRow(SQLModel, table=True):
    __tablename__ = "people"
    id: int | None = Field(default=None, primary_key=True)
    name: str
    org_id: int | None = Field(default=None, foreign_key="orgs.id")

2. Semantic models (what your app uses)

from ontosql import OntoModel, onto_property


class Organization(OntoModel):
    type_iri = "schema:Organization"
    iri_template = "https://data.example.org/org/{id}"

    id: int
    name: str = onto_property("schema:name")


class Person(OntoModel):
    type_iri = "schema:Person"
    iri_template = "https://data.example.org/person/{id}"

    id: int
    name: str = onto_property("schema:name")
    employer: Organization | None = onto_property("schema:worksFor")

3. Maps (explicit SQL bindings)

from ontosql import Map, OntoMapper


class OrganizationMap(OntoMapper[Organization]):
    entity = Organization
    id = Map(OrgRow.id)
    name = Map(OrgRow.name, property="schema:name")


class PersonMap(OntoMapper[Person]):
    entity = Person
    id = Map(PersonRow.id)
    name = Map(PersonRow.name, property="schema:name")
    employer = Map.nested(
        Organization,
        join=(PersonRow.org_id == OrgRow.id),
        target=OrgRow,
        nested_map=OrganizationMap,
        property="schema:worksFor",
        fk_column=PersonRow.org_id,
    )

See Cascade policies for nested write behavior (link, upsert, replace, ignore).

4. Session (CRUD)

from ontosql import OntoSession, paginate

with OntoSession(engine, maps=[PersonMap, OrganizationMap]) as session:
    ada = session.get(Person, id=1)
    team = session.find(Person, where=Person.employer.name.startswith("Analytical"))
    page = paginate(session, Person, limit=20, offset=0)

    new_person = session.save(Person.model_construct(name="Grace Hopper", id=None))
    new_person.name = "Grace M. Hopper"
    session.save(new_person)
    session.delete(new_person)

Async sessions use AsyncOntoSession with the same API — see Async guide and examples/person_org_async.py.

5. Export and import

print(ada.to_rdf(format="turtle"))
print(ada.to_jsonld())

from ontosql.import_ import import_from_jsonld  # trailing underscore (import is reserved)

restored = import_from_jsonld(ada.to_jsonld(), PersonMap)

Export walks OntoModel + onto_property metadata and serializes via TripleModel. Nested semantic objects become linked RDF resources.

Features

  • OntoModel + onto_property — semantic entities with ontology IRIs
  • OntoMapper / Map — declarative bindings to columns, joins, and nested entities
  • OntoSession / AsyncOntoSessionget, find, save, delete, paginate, identity map
  • Semantic queries — nested paths, contains / endswith, OrderBy(desc=True)
  • CascadePolicy — explicit nested write behavior on Map.nested (guide)
  • OntoRouter (ontosql[fastapi]) — auto CRUD routes + content negotiation
  • PrefixRegistry — CURIE expansion and JSON-LD @context
  • Exportto_jsonld() / to_rdf() on semantic instances
  • Importontosql.import_ hydrates OntoModel from RDF (FAQ)
  • Graph sync — mirror SQL writes to RDF graphs on commit (HYBRID.md)
  • SHACL — generate and validate shapes from maps (ontosql[shacl])
  • Prefix bundlesPrefixRegistry.curated() for schema.org / DC Terms

FastAPI

from fastapi import FastAPI
from ontosql.fastapi import OntoRouter, onto_session_lifespan

app = FastAPI()
onto_session_lifespan(app, engine, [PersonMap, OrganizationMap])
router = OntoRouter(maps=[PersonMap, OrganizationMap])
router.register(Person)
router.include_in(app)

See examples/person_org_api.py for a runnable API.

Production warning: OntoRouter is for development and demos only. POST/PATCH bodies are validated with generated Pydantic models, but there is no authentication, authorization, or rate limiting. Use AsyncOntoSession for async apps. See Security and SPECS.md.

Documentation

Getting started

Guides

Architecture and reference

Project

Development

See CONTRIBUTING.md and Releasing.

pip install -e ".[dev]"
ruff check src tests
ruff format src tests
ty check
pytest --cov=ontosql --cov-fail-under=90
mkdocs build --strict

License

MIT — see LICENSE.

Project details


Download files

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

Source Distribution

ontosql-0.4.0.tar.gz (76.4 kB view details)

Uploaded Source

Built Distribution

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

ontosql-0.4.0-py3-none-any.whl (51.6 kB view details)

Uploaded Python 3

File details

Details for the file ontosql-0.4.0.tar.gz.

File metadata

  • Download URL: ontosql-0.4.0.tar.gz
  • Upload date:
  • Size: 76.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ontosql-0.4.0.tar.gz
Algorithm Hash digest
SHA256 e1cf074f7d0113c0847d14189d3a5b037606ccdb8f664f49d5bb3229ae7e3b96
MD5 b28f1409f4b16122be28d5f0cf0b5ff6
BLAKE2b-256 728946d8268d029e1779d7e4ca14e50e12d4a0f53bcf542fd7634e794c0037bb

See more details on using hashes here.

File details

Details for the file ontosql-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: ontosql-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 51.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ontosql-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5a2c3b8bbe3fcfe907f5a8267f73ddb42cffea62b4a2d140822976df5ae5a35a
MD5 62ac031b0c31ec66b6c02a4fe71c1e23
BLAKE2b-256 56c235931a165d69cf39596777e4ece72a3e0cdc6a2429806d55183a86d36ee6

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