SPARQL ORM for Python — sessions, queries, and graph persistence on RDF stores
Project description
SparqlModel
SparqlModel — the SQLModel of SPARQL: typed RDF models, a persistent session, and Python filters that compile to SPARQL.
Define SPARQLModel classes, use with SPARQLSession() as session:, and work with graphs the way you would with a SQL ORM: put and get, nested relationships, a query builder, and optional remote stores.
Install
pip install sparqlmodel
Optional extras:
pip install "sparqlmodel[http]" # HttpStore (httpx)
pip install "sparqlmodel[fastapi]" # RDF response helpers
Development:
pip install -e ".[dev,http,fastapi]"
Quickstart
from sparqlmodel import Field, IRI, Relationship, SPARQLModel, SPARQLSession
class Organization(SPARQLModel):
rdf_type = "schema:Organization"
__prefixes__ = {"schema": "https://schema.org/"}
id: IRI
name: str = Field("schema:name")
class Person(SPARQLModel):
rdf_type = "schema:Person"
__prefixes__ = {"schema": "https://schema.org/"}
id: IRI
name: str = Field("schema:name")
works_for: Organization | None = Relationship(
"schema:worksFor", model=Organization
)
acme = Organization(id=IRI("urn:org:acme"), name="Acme Corp")
odos = Person(id=IRI("urn:person:odos"), name="Odos", works_for=acme)
with SPARQLSession() as session:
session.put(odos)
found = session.query(Person).where(Person.name == "Odos").first()
team = session.query(Person).where(Person.works_for.name == "Acme Corp").all()
full = session.get(Person, odos.id, depth=1)
Session API
SPARQLSession is a context manager: on success it flushes any pending put(..., flush=False) writes; on error it rolls back the pending queue; it closes HTTP stores when the block ends.
| Method | Purpose |
|---|---|
add(model) |
Insert triples; does not remove existing data for the subject |
put(model) |
Upsert with cascade and orphan cleanup for embedded resources |
delete(model) |
Remove owned triples for the root and composition tree |
get(Model, iri, depth=0) |
Load one entity; depth eager-loads relationships (0–2) |
query(Model).where(...) |
Find entities; filters compile to SPARQL |
execute(sparql) |
Raw SPARQL SELECT |
flush() / rollback_pending() |
Apply or discard pending put(..., flush=False) writes |
close() |
Close the backing store when it supports close() |
expire(Model, iri) |
Evict cached instances for an IRI |
put treats nested SPARQLModel values as composition (cascade delete and orphan cleanup). Use Relationship(..., cascade=False) or an IRI reference when the linked resource is owned elsewhere.
Query DSL
Filters compile to SPARQL against your store:
with SPARQLSession() as session:
session.query(Person).where(Person.name == "Odos").all()
session.query(Person).where(
(Person.name == "Odos") | (Person.name == "Ada")
).all()
session.query(Person).where(
Person.works_for.located_in.name == "Boston"
).all(depth=2)
session.query(Person).where(Person.name.in_(("Odos", "Ada"))).all()
session.query(Person).where(Person.name != "Other").use_not_exists_for_ne().all()
Supported operators include ==, !=, &, |, ordering comparisons, in_(), and multi-hop paths through relationships.
Stores
In-memory (default) — local rdflib graph, ideal for tests and prototypes:
with SPARQLSession() as session:
session.put(model)
HTTP — SPARQL 1.1 endpoint with a local mirror for cascade reads (sparqlmodel[http]):
from sparqlmodel import HttpStore, SPARQLSession
with SPARQLSession(store=HttpStore("http://localhost:3030/ds/sparql")) as session:
session.put(odos)
query / execute hit the remote endpoint; get and cascade logic use the mirror updated by this store’s writes. External changes visible only via SELECT are not visible to get until the mirror is updated.
Known limitations
- Multi-valued predicates — first value per predicate on load;
addcan duplicate literals addvsput—adddoes not remove stale triples; preferputfor upsertsHttpStore— mirror can lag behind the remote dataset (see above)- Shared embedded resources — same nested object linked from multiple roots is not deduplicated in memory on load
FastAPI
With sparqlmodel[fastapi], session management mirrors SQLModel / SQLAlchemy: register a shared store on the app, inject a per-request session with Depends, and use with SPARQLSession(...) inside the dependency.
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Request
from sparqlmodel import IRI
from sparqlmodel.fastapi import SessionDep, http_store_lifespan, init_app, negotiated_response
from sparqlmodel.stores.memory import MemoryStore
@asynccontextmanager
async def lifespan(app: FastAPI):
async with http_store_lifespan(app, "http://localhost:3030/ds/sparql"):
yield
# Or for tests / in-process: init_app(app, MemoryStore()); yield
app = FastAPI(lifespan=lifespan)
@app.get("/person/{iri}")
def person(iri: str, request: Request, session: SessionDep) -> object:
model = session.get(Person, IRI(iri))
if model is None:
raise HTTPException(status_code=404)
return negotiated_response(request, model)
SessionDep is Annotated[SPARQLSession, Depends(get_session)]. Each request opens with SPARQLSession(store=...) as session, flushes on success, and rolls back pending writes on error — the same lifecycle as using a session outside FastAPI.
Export
from sparqlmodel.serializers import export_model
print(export_model(odos, format="turtle"))
Documentation
- ORM guide — lifecycle, cascade, hydration
- Technical specification — API spec and production checklist
- Roadmap — milestones and SQLModel parity
- Production guide — HttpStore, sessions, pagination (planned)
- Project plan — vision and release strategy
- Changelog
Ecosystem
SparqlModel installs TripleModel (triplemodel>=0.9) as its RDF mapping engine: literals, terms, and file parse/serialize. Application code normally uses only sparqlmodel; reach for TripleModel directly when you need stateless Turtle/JSON-LD round-trips or library-style to_graph / sync_to_graph without a session.
See docs/ECOSYSTEM.md for package boundaries and the integration roadmap (wiring session I/O through TripleModel in upcoming releases).
License
MIT — see 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sparqlmodel-0.2.0.tar.gz.
File metadata
- Download URL: sparqlmodel-0.2.0.tar.gz
- Upload date:
- Size: 28.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e9253628f3c5baa46d2212556cc4e1abe815102552f6f84418195342e65c19f
|
|
| MD5 |
10ccc1f65d987116c4fc34c6ef39c1ce
|
|
| BLAKE2b-256 |
4a32a7deb47ac985965e10914c9ba38e6c200c9830b8ee8ae513589792a7d79c
|
File details
Details for the file sparqlmodel-0.2.0-py3-none-any.whl.
File metadata
- Download URL: sparqlmodel-0.2.0-py3-none-any.whl
- Upload date:
- Size: 35.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7c53076541373bf727d4554c274741d491047dc26cc0963a798fb9b5e44e55a1
|
|
| MD5 |
bcb77535c9a3b15b01a1a1f3234aa992
|
|
| BLAKE2b-256 |
c2658daa979d7cf2afb47a2e83fb747eb70feaa3d2a2c7a86399a9c5a322883f
|