Skip to main content

Lightweight Django-style ORM for SurrealDB using the official Python SDK. Async support with Pydantic validation.

Project description

Surreal ORM Lite

Python SurrealDB SDK License codecov

Surreal ORM Lite is a lightweight, Django-style ORM for SurrealDB that uses the official SurrealDB Python SDK. It provides a simple and intuitive interface for database operations with full async support and Pydantic validation.

Why This Project?

This ORM is designed to:

  • Use the official SurrealDB SDK (surrealdb>=1.0.8) for maximum compatibility
  • Stay lightweight with minimal dependencies
  • Keep up-to-date with SurrealDB and SDK releases
  • Provide Django-style query syntax that developers love

Requirements

Dependency Version
Python 3.11+
SurrealDB 2.6.0+
Official SDK surrealdb>=1.0.8
Pydantic >=2.12.5

Installation

pip install surreal-orm-lite

Or with uv:

uv add surreal-orm-lite

Quick Start

1. Configure the Connection

from surreal_orm_lite import SurrealDBConnectionManager

SurrealDBConnectionManager.set_connection(
    url="http://localhost:8000",
    user="root",
    password="root",
    namespace="my_namespace",
    database="my_database",
)

2. Define a Model

from surreal_orm_lite import BaseSurrealModel
from pydantic import Field

class User(BaseSurrealModel):
    id: str | None = None
    name: str = Field(..., max_length=100)
    email: str
    age: int = Field(..., ge=0)

3. CRUD Operations

# Create
user = User(name="Alice", email="alice@example.com", age=30)
await user.save()

# Read
user = await User.objects().get("alice_id")
users = await User.objects().filter(age__gte=18).exec()

# Update
user.age = 31
await user.update()

# Or partial update
await user.merge(age=31)

# Delete
await user.delete()

4. QuerySet Methods

# Filter with Django-style lookups
users = await User.objects().filter(
    age__gte=18,
    name__startswith="A"
).exec()

# Ordering (with -field shorthand for DESC)
users = await User.objects().order_by("name").exec()
users = await User.objects().order_by("-age", "name").exec()

# Pagination
users = await User.objects().limit(10).offset(20).exec()

# Select specific fields
results = await User.objects().select("name", "email").exec()

# Get first result
user = await User.objects().filter(name="Alice").first()

# Get all records
all_users = await User.objects().all()

# Custom query
results = await User.objects().query(
    "SELECT * FROM User WHERE age > $min_age",
    {"min_age": 21}
)

Features

Feature Status
Async/await support
Pydantic validation
CRUD operations
QuerySet with filters
Django-style lookups
Custom primary keys
HTTP connections
WebSocket connections
Aggregations
GROUP BY
Model Signals
Raw SurrealQL queries
Q Objects (OR/AND/NOT)
Parameterized filters
Bulk operations
-field ordering

Supported Filter Lookups

  • exact (default)
  • gt, gte, lt, lte
  • in, not_in
  • contains, icontains, not_contains
  • containsall, containsany
  • startswith, istartswith
  • endswith, iendswith
  • like, ilike
  • match, regex, iregex
  • isnull

5. Q Objects (Complex Queries)

from surreal_orm_lite import Q

# OR queries
users = await User.objects().filter(Q(name="Alice") | Q(name="Bob")).exec()

# NOT queries
active = await User.objects().filter(~Q(status="banned")).exec()

# Complex combinations
results = await User.objects().filter(
    Q(age__gte=18) & (Q(role="admin") | Q(role="mod"))
).exec()

# Mix Q objects with keyword filters
results = await User.objects().filter(
    Q(role="admin") | Q(role="mod"),
    age__gte=25
).exec()

6. Bulk Operations

# Bulk create
users = [User(name="Alice", age=30), User(name="Bob", age=25)]
created = await User.objects().bulk_create(users)

# Bulk update (returns count of updated records)
count = await User.objects().filter(status="pending").bulk_update(status="active")

# Bulk delete (returns count of deleted records)
count = await User.objects().filter(status="inactive").bulk_delete()

7. Aggregations

from surreal_orm_lite import Count, Sum, Avg, Min, Max

# Simple aggregations
count = await User.objects().count()
total = await Order.objects().sum("amount")
avg_age = await User.objects().avg("age")
max_price = await Product.objects().max("price")
min_price = await Product.objects().min("price")

# Check existence
has_admins = await User.objects().filter(role="admin").exists()

# GROUP BY with annotations
results = await User.objects().values("status").annotate(count=Count()).exec()
# [{"status": "active", "count": 42}, {"status": "inactive", "count": 8}]

# Raw SurrealQL queries
results = await User.raw_query(
    "SELECT * FROM User WHERE age > $min_age",
    variables={"min_age": 18}
)

8. Model Signals

from surreal_orm_lite import pre_save, post_save, pre_delete, post_delete

@post_save.connect(User)
async def on_user_saved(sender, instance, created, **kwargs):
    """Called after every User save."""
    if created:
        await send_welcome_email(instance.email)
    await invalidate_cache(f"user:{instance.id}")

@pre_delete.connect(User)
async def on_user_deleting(sender, instance, **kwargs):
    """Called before User deletion."""
    await archive_user_data(instance.id)

Available signals:

Signal When Extra kwargs
pre_save Before save()
post_save After save() created
pre_update Before update()/merge() update_fields
post_update After update()/merge() update_fields
pre_delete Before delete()
post_delete After delete()
around_save Wraps save()
around_update Wraps update()/merge() update_fields
around_delete Wraps delete()

Around signals use async generators to wrap operations:

from surreal_orm_lite import around_save

@around_save.connect(User)
async def time_user_save(sender, instance, **kwargs):
    import time
    start = time.time()
    yield  # save() executes here
    duration = time.time() - start
    print(f"Save took {duration:.3f}s")

Configuration Options

Custom Primary Key

from surreal_orm_lite import BaseSurrealModel, SurrealConfigDict

class Product(BaseSurrealModel):
    model_config = SurrealConfigDict(primary_key="sku")

    sku: str
    name: str
    price: float

Context Manager

async with SurrealDBConnectionManager():
    users = await User.objects().all()
# Connection automatically closed

Compatibility

This ORM is tested and compatible with:

SurrealDB Version SDK Version Status
2.6.0 1.0.8 ✅ Tested
2.5.x 1.0.8 ✅ Compatible

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m "Add amazing feature")
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Roadmap

Version Theme Status
v0.2.x Core ORM (CRUD, QuerySet) ✅ Released
v0.3.0 Aggregations & Utilities ✅ Released
v0.4.0 Model Signals ✅ Released
v0.5.0 Bulk Operations & Q Objects ✅ Released
v0.6.0 Relations & Graph 📋 Next
v0.7.0 Transactions ORM 📋 Planned
v0.8.0 SurrealFunc & Computed Fields 📋 Planned
v0.9.0 Field Aliases & DX 📋 Planned
v1.0.0 Production Ready 📋 Planned

See docs/ROADMAP.md for full details.


SurrealDB-ORM-lite vs SurrealDB-ORM

This project prioritizes stability and compatibility with the official SurrealDB Python SDK. The full SurrealDB-ORM uses a custom SDK for advanced features.

Feature ORM-lite (official SDK) ORM (custom SDK)
CRUD & QuerySet
Aggregations & GROUP BY
Model Signals
Bulk Operations
Q Objects (OR/AND/NOT)
Parameterized Filters
Relations & Graph v0.6.0
FETCH clause v0.6.0
Transactions (tx=) v0.7.0
SurrealFunc & Computed v0.8.0
Field Aliases v0.9.0
Retry, Logging, Metrics v0.10.0
Live Models / CDC
Vector / Full-Text Search
Hybrid Search (RRF)
Migrations & CLI
JWT Authentication
Schema Introspection
Connection Pool
CBOR Protocol
Subqueries & Query Cache
Geospatial Fields
DEFINE EVENT
Test Fixtures & Factories
Atomic Array Operations

Choose ORM-lite if you want the official SDK, minimal dependencies, and core ORM features.

Choose ORM if you need live queries, migrations, authentication, vector search, or advanced features.


License

MIT License - see LICENSE for details.


Author

Yannick Croteau GitHub: @EulogySnowfall


Related Projects

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

surreal_orm_lite-0.5.0.tar.gz (24.5 kB view details)

Uploaded Source

Built Distribution

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

surreal_orm_lite-0.5.0-py3-none-any.whl (25.7 kB view details)

Uploaded Python 3

File details

Details for the file surreal_orm_lite-0.5.0.tar.gz.

File metadata

  • Download URL: surreal_orm_lite-0.5.0.tar.gz
  • Upload date:
  • Size: 24.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for surreal_orm_lite-0.5.0.tar.gz
Algorithm Hash digest
SHA256 24106d7cc9bad5f09e692fa698b80dd0295d06aabb7ad126c140f5a7c068ad76
MD5 c1ab43956166f8bee856c0cb261fe739
BLAKE2b-256 c88f88664cacc6d52248f71d19c549718d0eb27f8a640e101908f3d4dcd82f4d

See more details on using hashes here.

Provenance

The following attestation bundles were made for surreal_orm_lite-0.5.0.tar.gz:

Publisher: publish.yml on EulogySnowfall/SurrealDB-ORM-lite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file surreal_orm_lite-0.5.0-py3-none-any.whl.

File metadata

File hashes

Hashes for surreal_orm_lite-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6acf7f5608abd69af44eb74b83aa7058000f46001d65e2a4f4d709693791d075
MD5 aebf998f9cf229a7b7df6339aa8e283c
BLAKE2b-256 6dfc9a3927af218eeee92d4f0d733ab4ac3ceb30dcc02b8b2eca98151de3915b

See more details on using hashes here.

Provenance

The following attestation bundles were made for surreal_orm_lite-0.5.0-py3-none-any.whl:

Publisher: publish.yml on EulogySnowfall/SurrealDB-ORM-lite

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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