surrealdb-orm 0.1.4__tar.gz → 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of surrealdb-orm might be problematic. Click here for more details.
- {surrealdb_orm-0.1.4 → surrealdb_orm-0.5.1}/.gitignore +4 -1
- {surrealdb_orm-0.1.4 → surrealdb_orm-0.5.1}/LICENSE +1 -1
- surrealdb_orm-0.5.1/Makefile +200 -0
- surrealdb_orm-0.5.1/PKG-INFO +465 -0
- surrealdb_orm-0.5.1/README.md +412 -0
- {surrealdb_orm-0.1.4 → surrealdb_orm-0.5.1}/pyproject.toml +39 -14
- surrealdb_orm-0.5.1/src/surreal_orm/__init__.py +81 -0
- surrealdb_orm-0.5.1/src/surreal_orm/aggregations.py +164 -0
- surrealdb_orm-0.5.1/src/surreal_orm/auth/__init__.py +15 -0
- surrealdb_orm-0.5.1/src/surreal_orm/auth/access.py +167 -0
- surrealdb_orm-0.5.1/src/surreal_orm/auth/mixins.py +302 -0
- surrealdb_orm-0.5.1/src/surreal_orm/cli/__init__.py +15 -0
- surrealdb_orm-0.5.1/src/surreal_orm/cli/commands.py +369 -0
- {surrealdb_orm-0.1.4 → surrealdb_orm-0.5.1}/src/surreal_orm/connection_manager.py +58 -18
- surrealdb_orm-0.5.1/src/surreal_orm/fields/__init__.py +36 -0
- surrealdb_orm-0.5.1/src/surreal_orm/fields/encrypted.py +166 -0
- surrealdb_orm-0.5.1/src/surreal_orm/fields/relation.py +465 -0
- surrealdb_orm-0.5.1/src/surreal_orm/migrations/__init__.py +51 -0
- surrealdb_orm-0.5.1/src/surreal_orm/migrations/executor.py +380 -0
- surrealdb_orm-0.5.1/src/surreal_orm/migrations/generator.py +272 -0
- surrealdb_orm-0.5.1/src/surreal_orm/migrations/introspector.py +305 -0
- surrealdb_orm-0.5.1/src/surreal_orm/migrations/migration.py +188 -0
- surrealdb_orm-0.5.1/src/surreal_orm/migrations/operations.py +531 -0
- surrealdb_orm-0.5.1/src/surreal_orm/migrations/state.py +406 -0
- surrealdb_orm-0.5.1/src/surreal_orm/model_base.py +688 -0
- surrealdb_orm-0.5.1/src/surreal_orm/query_set.py +1067 -0
- surrealdb_orm-0.5.1/src/surreal_orm/relations.py +645 -0
- surrealdb_orm-0.5.1/src/surreal_orm/surreal_function.py +95 -0
- surrealdb_orm-0.5.1/src/surreal_orm/surreal_ql.py +113 -0
- surrealdb_orm-0.5.1/src/surreal_orm/types.py +86 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/README.md +79 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/__init__.py +151 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/connection/__init__.py +17 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/connection/base.py +516 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/connection/http.py +421 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/connection/pool.py +244 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/connection/websocket.py +519 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/exceptions.py +71 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/functions.py +607 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/protocol/__init__.py +13 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/protocol/rpc.py +218 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/py.typed +0 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/pyproject.toml +49 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/streaming/__init__.py +31 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/streaming/change_feed.py +278 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/streaming/live_query.py +265 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/streaming/live_select.py +369 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/transaction.py +386 -0
- surrealdb_orm-0.5.1/src/surreal_sdk/types.py +346 -0
- surrealdb_orm-0.1.4/Makefile +0 -48
- surrealdb_orm-0.1.4/PKG-INFO +0 -184
- surrealdb_orm-0.1.4/README.md +0 -139
- surrealdb_orm-0.1.4/src/__init__.py +0 -3
- surrealdb_orm-0.1.4/src/surreal_orm/__init__.py +0 -12
- surrealdb_orm-0.1.4/src/surreal_orm/model_base.py +0 -202
- surrealdb_orm-0.1.4/src/surreal_orm/query_set.py +0 -491
- {surrealdb_orm-0.1.4 → surrealdb_orm-0.5.1}/src/surreal_orm/constants.py +0 -0
- {surrealdb_orm-0.1.4 → surrealdb_orm-0.5.1}/src/surreal_orm/enum.py +0 -0
- {surrealdb_orm-0.1.4 → surrealdb_orm-0.5.1}/src/surreal_orm/py.typed +0 -0
- {surrealdb_orm-0.1.4 → surrealdb_orm-0.5.1}/src/surreal_orm/utils.py +0 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
.DEFAULT_GOAL := all
|
|
2
|
+
sources = src
|
|
3
|
+
COMPOSE_FILE = devops/docker-compose.yml
|
|
4
|
+
|
|
5
|
+
.PHONY: .uv # Check that uv is installed
|
|
6
|
+
.uv:
|
|
7
|
+
@uv --version || echo 'Please install uv: https://docs.astral.sh/uv/getting-started/installation/'
|
|
8
|
+
|
|
9
|
+
.PHONY: install # Install the package, dependencies, and pre-commit for local development
|
|
10
|
+
install: .uv
|
|
11
|
+
uv sync --frozen --group lint
|
|
12
|
+
uv run pre-commit install --install-hooks
|
|
13
|
+
|
|
14
|
+
.PHONY: format # Format the code
|
|
15
|
+
format:
|
|
16
|
+
uv run ruff format
|
|
17
|
+
uv run ruff check --fix --fix-only
|
|
18
|
+
|
|
19
|
+
.PHONY: lint # Lint the code
|
|
20
|
+
lint:
|
|
21
|
+
uv run ruff format --check
|
|
22
|
+
uv run ruff check
|
|
23
|
+
|
|
24
|
+
.PHONY: mypy
|
|
25
|
+
mypy:
|
|
26
|
+
uv run python -m mypy $(sources)
|
|
27
|
+
|
|
28
|
+
.PHONY: typecheck
|
|
29
|
+
typecheck:
|
|
30
|
+
uv run pyright
|
|
31
|
+
|
|
32
|
+
.PHONY: test
|
|
33
|
+
test:
|
|
34
|
+
uv run pytest -m "not integration"
|
|
35
|
+
|
|
36
|
+
.PHONY: test-unit # Run unit tests only (no SurrealDB required)
|
|
37
|
+
test-unit:
|
|
38
|
+
uv run pytest -m "not integration" tests/
|
|
39
|
+
|
|
40
|
+
.PHONY: test-sdk # Run SDK tests only
|
|
41
|
+
test-sdk:
|
|
42
|
+
uv run pytest -m "not integration" tests/sdk/
|
|
43
|
+
|
|
44
|
+
.PHONY: test-integration # Run integration tests (requires SurrealDB)
|
|
45
|
+
test-integration: db-up
|
|
46
|
+
uv run pytest -m integration tests/
|
|
47
|
+
|
|
48
|
+
.PHONY: test-all # Run all tests including integration
|
|
49
|
+
test-all: db-up
|
|
50
|
+
uv run pytest tests/
|
|
51
|
+
|
|
52
|
+
.PHONY: test-all-python # Run tests on Python 3.12 to 3.14
|
|
53
|
+
test-all-python:
|
|
54
|
+
uv run --python 3.12 coverage run -p -m pytest -m "not integration" --junitxml=junit.xml -o junit_family=legacy
|
|
55
|
+
UV_PROJECT_ENVIRONMENT=.venv313 uv run --python 3.13 coverage run -p -m pytest -m "not integration"
|
|
56
|
+
UV_PROJECT_ENVIRONMENT=.venv314 uv run --python 3.14 coverage run -p -m pytest -m "not integration"
|
|
57
|
+
@uv run coverage xml -o coverage.xml
|
|
58
|
+
@uv run coverage report
|
|
59
|
+
|
|
60
|
+
.PHONY: html # Generate HTML coverage report
|
|
61
|
+
html: test-all-python
|
|
62
|
+
uv run coverage html -d htmlcov
|
|
63
|
+
|
|
64
|
+
# =============================================================================
|
|
65
|
+
# Docker commands for SurrealDB
|
|
66
|
+
# =============================================================================
|
|
67
|
+
|
|
68
|
+
.PHONY: db-up # Start SurrealDB container for testing
|
|
69
|
+
db-up:
|
|
70
|
+
docker compose -f $(COMPOSE_FILE) up -d surrealdb-test
|
|
71
|
+
@./devops/wait-for-healthy.sh localhost 8001 30
|
|
72
|
+
@echo "SurrealDB test instance ready on port 8001"
|
|
73
|
+
|
|
74
|
+
.PHONY: db-down # Stop SurrealDB containers
|
|
75
|
+
db-down:
|
|
76
|
+
docker compose -f $(COMPOSE_FILE) down
|
|
77
|
+
|
|
78
|
+
.PHONY: db-dev # Start development SurrealDB (persistent)
|
|
79
|
+
db-dev:
|
|
80
|
+
docker compose -f $(COMPOSE_FILE) up -d surrealdb
|
|
81
|
+
@./devops/wait-for-healthy.sh localhost 8000 30
|
|
82
|
+
@echo "SurrealDB dev instance ready on port 8000 (persistent)"
|
|
83
|
+
|
|
84
|
+
.PHONY: db-cluster # Start multi-node cluster for K8s simulation
|
|
85
|
+
db-cluster:
|
|
86
|
+
docker compose -f $(COMPOSE_FILE) --profile cluster up -d
|
|
87
|
+
@./devops/wait-for-healthy.sh localhost 8002 30
|
|
88
|
+
@./devops/wait-for-healthy.sh localhost 8003 30
|
|
89
|
+
@./devops/wait-for-healthy.sh localhost 8004 30
|
|
90
|
+
@echo "SurrealDB cluster ready on ports 8002, 8003, 8004"
|
|
91
|
+
|
|
92
|
+
.PHONY: db-logs # Show SurrealDB logs
|
|
93
|
+
db-logs:
|
|
94
|
+
docker compose -f $(COMPOSE_FILE) logs -f surrealdb-test
|
|
95
|
+
|
|
96
|
+
.PHONY: db-logs-dev # Show dev SurrealDB logs
|
|
97
|
+
db-logs-dev:
|
|
98
|
+
docker compose -f $(COMPOSE_FILE) logs -f surrealdb
|
|
99
|
+
|
|
100
|
+
.PHONY: db-shell # Open SurrealDB SQL shell (test instance)
|
|
101
|
+
db-shell:
|
|
102
|
+
docker compose -f $(COMPOSE_FILE) exec surrealdb-test /surreal sql --endpoint http://localhost:8000 --username root --password root --namespace test --database test
|
|
103
|
+
|
|
104
|
+
.PHONY: db-shell-dev # Open SurrealDB SQL shell (dev instance)
|
|
105
|
+
db-shell-dev:
|
|
106
|
+
docker compose -f $(COMPOSE_FILE) exec surrealdb /surreal sql --endpoint http://localhost:8000 --username root --password root --namespace test --database test
|
|
107
|
+
|
|
108
|
+
.PHONY: db-setup # Setup test database schema
|
|
109
|
+
db-setup: db-up
|
|
110
|
+
@./devops/setup-test-db.sh localhost 8001
|
|
111
|
+
|
|
112
|
+
.PHONY: db-status # Show status of all SurrealDB containers
|
|
113
|
+
db-status:
|
|
114
|
+
@docker compose -f $(COMPOSE_FILE) ps
|
|
115
|
+
|
|
116
|
+
.PHONY: db-clean # Remove all SurrealDB containers and volumes
|
|
117
|
+
db-clean:
|
|
118
|
+
docker compose -f $(COMPOSE_FILE) down -v --remove-orphans
|
|
119
|
+
|
|
120
|
+
# =============================================================================
|
|
121
|
+
# CI targets
|
|
122
|
+
# =============================================================================
|
|
123
|
+
|
|
124
|
+
.PHONY: ci-db-up # Start SurrealDB for CI (optimized)
|
|
125
|
+
ci-db-up:
|
|
126
|
+
docker compose -f $(COMPOSE_FILE) up -d surrealdb-ci
|
|
127
|
+
@./devops/wait-for-healthy.sh localhost 8000 60
|
|
128
|
+
@echo "SurrealDB CI instance ready"
|
|
129
|
+
|
|
130
|
+
.PHONY: ci-test # Run all tests for CI
|
|
131
|
+
ci-test: ci-db-up
|
|
132
|
+
uv run pytest tests/ --junitxml=junit.xml -o junit_family=legacy
|
|
133
|
+
uv run coverage xml -o coverage.xml
|
|
134
|
+
|
|
135
|
+
.PHONY: ci-lint # Run linting for CI
|
|
136
|
+
ci-lint:
|
|
137
|
+
uv run ruff format --check
|
|
138
|
+
uv run ruff check
|
|
139
|
+
uv run python -m mypy $(sources)
|
|
140
|
+
|
|
141
|
+
.PHONY: ci # Full CI pipeline
|
|
142
|
+
ci: ci-lint ci-test
|
|
143
|
+
|
|
144
|
+
# =============================================================================
|
|
145
|
+
# Utilities
|
|
146
|
+
# =============================================================================
|
|
147
|
+
|
|
148
|
+
.PHONY: clean # Clean up generated files
|
|
149
|
+
clean:
|
|
150
|
+
rm -rf .pytest_cache .mypy_cache .ruff_cache htmlcov .coverage coverage.xml junit.xml
|
|
151
|
+
rm -rf dist build *.egg-info
|
|
152
|
+
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
|
|
153
|
+
|
|
154
|
+
.PHONY: clean-all # Clean everything including Docker
|
|
155
|
+
clean-all: clean db-clean
|
|
156
|
+
|
|
157
|
+
.PHONY: all
|
|
158
|
+
all: format mypy lint typecheck test-all-python
|
|
159
|
+
|
|
160
|
+
.PHONY: help # Show this help
|
|
161
|
+
help:
|
|
162
|
+
@echo "Available targets:"
|
|
163
|
+
@echo ""
|
|
164
|
+
@echo " Development:"
|
|
165
|
+
@echo " install Install dependencies and pre-commit hooks"
|
|
166
|
+
@echo " format Format code with ruff"
|
|
167
|
+
@echo " lint Check code with ruff"
|
|
168
|
+
@echo " mypy Run mypy type checker"
|
|
169
|
+
@echo " typecheck Run pyright type checker"
|
|
170
|
+
@echo ""
|
|
171
|
+
@echo " Testing:"
|
|
172
|
+
@echo " test Run unit tests (no SurrealDB required)"
|
|
173
|
+
@echo " test-unit Run unit tests only"
|
|
174
|
+
@echo " test-sdk Run SDK tests only"
|
|
175
|
+
@echo " test-integration Run integration tests (starts SurrealDB)"
|
|
176
|
+
@echo " test-all Run all tests including integration"
|
|
177
|
+
@echo " test-all-python Run tests on Python 3.12-3.14"
|
|
178
|
+
@echo ""
|
|
179
|
+
@echo " Docker/SurrealDB:"
|
|
180
|
+
@echo " db-up Start test SurrealDB (port 8001)"
|
|
181
|
+
@echo " db-dev Start dev SurrealDB (port 8000, persistent)"
|
|
182
|
+
@echo " db-cluster Start 3-node cluster (ports 8002-8004)"
|
|
183
|
+
@echo " db-down Stop all SurrealDB containers"
|
|
184
|
+
@echo " db-logs Show test SurrealDB logs"
|
|
185
|
+
@echo " db-shell Open test SurrealDB SQL shell"
|
|
186
|
+
@echo " db-setup Setup test database schema"
|
|
187
|
+
@echo " db-status Show container status"
|
|
188
|
+
@echo " db-clean Remove containers and volumes"
|
|
189
|
+
@echo ""
|
|
190
|
+
@echo " CI:"
|
|
191
|
+
@echo " ci Full CI pipeline (lint + test)"
|
|
192
|
+
@echo " ci-db-up Start SurrealDB for CI"
|
|
193
|
+
@echo " ci-test Run tests for CI"
|
|
194
|
+
@echo " ci-lint Run linting for CI"
|
|
195
|
+
@echo ""
|
|
196
|
+
@echo " Other:"
|
|
197
|
+
@echo " clean Clean generated files"
|
|
198
|
+
@echo " clean-all Clean everything including Docker"
|
|
199
|
+
@echo " all Run format, lint, typecheck, and tests"
|
|
200
|
+
@echo " help Show this help"
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: surrealdb-orm
|
|
3
|
+
Version: 0.5.1
|
|
4
|
+
Summary: SurrealDB ORM as 'DJango style' for Python with async support. Works with pydantic validation.
|
|
5
|
+
Project-URL: Homepage, https://github.com/EulogySnowfall/SurrealDB-ORM
|
|
6
|
+
Project-URL: Documentation, https://github.com/EulogySnowfall/SurrealDB-ORM
|
|
7
|
+
Project-URL: Repository, https://github.com/EulogySnowfall/SurrealDB-ORM.git
|
|
8
|
+
Project-URL: Issues, https://github.com/EulogySnowfall/SurrealDB-ORM/issues
|
|
9
|
+
Author-email: Yannick Croteau <croteau.yannick@gmail.com>
|
|
10
|
+
License: # MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2025-2026 Yannick Croteau
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Operating System :: OS Independent
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
39
|
+
Classifier: Topic :: Database
|
|
40
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
41
|
+
Requires-Python: >=3.12
|
|
42
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
43
|
+
Requires-Dist: httpx>=0.27.0
|
|
44
|
+
Requires-Dist: pydantic>=2.10.5
|
|
45
|
+
Provides-Extra: all
|
|
46
|
+
Requires-Dist: cbor2>=5.6.0; extra == 'all'
|
|
47
|
+
Requires-Dist: click>=8.1.0; extra == 'all'
|
|
48
|
+
Provides-Extra: cbor
|
|
49
|
+
Requires-Dist: cbor2>=5.6.0; extra == 'cbor'
|
|
50
|
+
Provides-Extra: cli
|
|
51
|
+
Requires-Dist: click>=8.1.0; extra == 'cli'
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
# SurrealDB-ORM
|
|
55
|
+
|
|
56
|
+

|
|
57
|
+

|
|
58
|
+
[](https://codecov.io/gh/EulogySnowfall/SurrealDB-ORM)
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
> **Alpha Software** - APIs may change. Use in non-production environments.
|
|
62
|
+
|
|
63
|
+
**SurrealDB-ORM** is a Django-style ORM for [SurrealDB](https://surrealdb.com/) with async support, Pydantic validation, and JWT authentication.
|
|
64
|
+
|
|
65
|
+
**Includes a custom SDK (`surreal_sdk`)** - Zero dependency on the official `surrealdb` package!
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## What's New in 0.4.0
|
|
70
|
+
|
|
71
|
+
- **Relations & Graph Traversal** - Django-style relation definitions with SurrealDB graph support
|
|
72
|
+
- `ForeignKey`, `ManyToMany`, `Relation` field types
|
|
73
|
+
- Relation operations: `add()`, `remove()`, `set()`, `clear()`, `all()`, `filter()`, `count()`
|
|
74
|
+
- Model methods: `relate()`, `remove_relation()`, `get_related()`
|
|
75
|
+
- QuerySet extensions: `select_related()`, `prefetch_related()`, `traverse()`, `graph_query()`
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Table of Contents
|
|
80
|
+
|
|
81
|
+
- [Installation](#installation)
|
|
82
|
+
- [Quick Start](#quick-start)
|
|
83
|
+
- [Using the SDK (Recommended)](#using-the-sdk-recommended)
|
|
84
|
+
- [Using the ORM](#using-the-orm)
|
|
85
|
+
- [SDK Features](#sdk-features)
|
|
86
|
+
- [Connections](#connections)
|
|
87
|
+
- [Transactions](#transactions)
|
|
88
|
+
- [Typed Functions](#typed-functions)
|
|
89
|
+
- [Live Queries](#live-queries)
|
|
90
|
+
- [ORM Features](#orm-features)
|
|
91
|
+
- [CLI Commands](#cli-commands)
|
|
92
|
+
- [Documentation](#documentation)
|
|
93
|
+
- [Contributing](#contributing)
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Installation
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Basic installation
|
|
101
|
+
pip install surrealdb-orm
|
|
102
|
+
|
|
103
|
+
# With CLI support
|
|
104
|
+
pip install surrealdb-orm[cli]
|
|
105
|
+
|
|
106
|
+
# Full installation (CLI + CBOR)
|
|
107
|
+
pip install surrealdb-orm[all]
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Requirements:** Python 3.12+ | SurrealDB 2.6.0+
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Quick Start
|
|
115
|
+
|
|
116
|
+
### Using the SDK (Recommended)
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from surreal_sdk import SurrealDB
|
|
120
|
+
|
|
121
|
+
async def main():
|
|
122
|
+
# HTTP connection (stateless, ideal for microservices)
|
|
123
|
+
async with SurrealDB.http("http://localhost:8000", "namespace", "database") as db:
|
|
124
|
+
await db.signin("root", "root")
|
|
125
|
+
|
|
126
|
+
# CRUD operations
|
|
127
|
+
user = await db.create("users", {"name": "Alice", "age": 30})
|
|
128
|
+
users = await db.query("SELECT * FROM users WHERE age > $min", {"min": 18})
|
|
129
|
+
|
|
130
|
+
# Atomic transactions
|
|
131
|
+
async with db.transaction() as tx:
|
|
132
|
+
await tx.create("accounts:alice", {"balance": 1000})
|
|
133
|
+
await tx.create("accounts:bob", {"balance": 500})
|
|
134
|
+
# Auto-commit on success, auto-rollback on exception
|
|
135
|
+
|
|
136
|
+
# Built-in functions with typed API
|
|
137
|
+
result = await db.fn.math.sqrt(16) # Returns 4.0
|
|
138
|
+
now = await db.fn.time.now() # Current timestamp
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Using the ORM
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from surreal_orm import BaseSurrealModel, SurrealDBConnectionManager
|
|
145
|
+
|
|
146
|
+
# 1. Define your model
|
|
147
|
+
class User(BaseSurrealModel):
|
|
148
|
+
id: str | None = None
|
|
149
|
+
name: str
|
|
150
|
+
email: str
|
|
151
|
+
age: int = 0
|
|
152
|
+
|
|
153
|
+
# 2. Configure connection
|
|
154
|
+
SurrealDBConnectionManager.set_connection(
|
|
155
|
+
url="http://localhost:8000",
|
|
156
|
+
user="root",
|
|
157
|
+
password="root",
|
|
158
|
+
namespace="myapp",
|
|
159
|
+
database="main",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# 3. CRUD operations
|
|
163
|
+
user = User(name="Alice", email="alice@example.com", age=30)
|
|
164
|
+
await user.save()
|
|
165
|
+
|
|
166
|
+
users = await User.objects().filter(age__gte=18).order_by("name").limit(10).exec()
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## SDK Features
|
|
172
|
+
|
|
173
|
+
### Connections
|
|
174
|
+
|
|
175
|
+
| Type | Use Case | Features |
|
|
176
|
+
| ------------- | ------------------------ | ------------------------ |
|
|
177
|
+
| **HTTP** | Microservices, REST APIs | Stateless, simple |
|
|
178
|
+
| **WebSocket** | Real-time apps | Live queries, persistent |
|
|
179
|
+
| **Pool** | High-throughput | Connection reuse |
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from surreal_sdk import SurrealDB, HTTPConnection, WebSocketConnection
|
|
183
|
+
|
|
184
|
+
# HTTP (stateless)
|
|
185
|
+
async with SurrealDB.http("http://localhost:8000", "ns", "db") as db:
|
|
186
|
+
await db.signin("root", "root")
|
|
187
|
+
|
|
188
|
+
# WebSocket (stateful, real-time)
|
|
189
|
+
async with SurrealDB.ws("ws://localhost:8000", "ns", "db") as db:
|
|
190
|
+
await db.signin("root", "root")
|
|
191
|
+
await db.live("orders", callback=on_order_change)
|
|
192
|
+
|
|
193
|
+
# Connection Pool
|
|
194
|
+
async with SurrealDB.pool("http://localhost:8000", "ns", "db", size=10) as pool:
|
|
195
|
+
await pool.set_credentials("root", "root")
|
|
196
|
+
async with pool.acquire() as conn:
|
|
197
|
+
await conn.query("SELECT * FROM users")
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Transactions
|
|
201
|
+
|
|
202
|
+
Atomic transactions with automatic commit/rollback:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
# WebSocket: Immediate execution with server-side transaction
|
|
206
|
+
async with db.transaction() as tx:
|
|
207
|
+
await tx.update("players:abc", {"is_ready": True})
|
|
208
|
+
await tx.update("game_tables:xyz", {"ready_count": "+=1"})
|
|
209
|
+
# Statements execute immediately
|
|
210
|
+
# COMMIT on success, CANCEL on exception
|
|
211
|
+
|
|
212
|
+
# HTTP: Batched execution (all-or-nothing)
|
|
213
|
+
async with db.transaction() as tx:
|
|
214
|
+
await tx.create("orders:1", {"total": 100})
|
|
215
|
+
await tx.create("payments:1", {"amount": 100})
|
|
216
|
+
# Statements queued, executed atomically at commit
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Transaction Methods:**
|
|
220
|
+
|
|
221
|
+
- `tx.query(sql, vars)` - Execute raw SurrealQL
|
|
222
|
+
- `tx.create(thing, data)` - Create record
|
|
223
|
+
- `tx.update(thing, data)` - Replace record
|
|
224
|
+
- `tx.delete(thing)` - Delete record
|
|
225
|
+
- `tx.relate(from, edge, to)` - Create graph edge
|
|
226
|
+
- `tx.commit()` - Explicit commit
|
|
227
|
+
- `tx.rollback()` - Explicit rollback
|
|
228
|
+
|
|
229
|
+
### Typed Functions
|
|
230
|
+
|
|
231
|
+
Fluent API for SurrealDB functions:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
# Built-in functions (namespace::function)
|
|
235
|
+
sqrt = await db.fn.math.sqrt(16) # 4.0
|
|
236
|
+
now = await db.fn.time.now() # datetime
|
|
237
|
+
length = await db.fn.string.len("hello") # 5
|
|
238
|
+
sha = await db.fn.crypto.sha256("data") # hash string
|
|
239
|
+
|
|
240
|
+
# Custom user-defined functions (fn::function)
|
|
241
|
+
result = await db.fn.my_custom_function(arg1, arg2)
|
|
242
|
+
# Executes: RETURN fn::my_custom_function($arg0, $arg1)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Available Namespaces:**
|
|
246
|
+
`array`, `crypto`, `duration`, `geo`, `http`, `math`, `meta`, `object`, `parse`, `rand`, `session`, `string`, `time`, `type`, `vector`
|
|
247
|
+
|
|
248
|
+
### Live Queries
|
|
249
|
+
|
|
250
|
+
Real-time updates via WebSocket:
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
from surreal_sdk import LiveQuery, LiveNotification, LiveAction
|
|
254
|
+
|
|
255
|
+
async def on_change(notification: LiveNotification):
|
|
256
|
+
if notification.action == LiveAction.CREATE:
|
|
257
|
+
print(f"New record: {notification.result}")
|
|
258
|
+
elif notification.action == LiveAction.UPDATE:
|
|
259
|
+
print(f"Updated: {notification.result}")
|
|
260
|
+
elif notification.action == LiveAction.DELETE:
|
|
261
|
+
print(f"Deleted: {notification.result}")
|
|
262
|
+
|
|
263
|
+
live = LiveQuery(ws_conn, "orders")
|
|
264
|
+
await live.subscribe(on_change)
|
|
265
|
+
# ... records changes trigger callbacks ...
|
|
266
|
+
await live.unsubscribe()
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## ORM Features
|
|
272
|
+
|
|
273
|
+
### QuerySet with Django-style Lookups
|
|
274
|
+
|
|
275
|
+
```python
|
|
276
|
+
# Filter with lookups
|
|
277
|
+
users = await User.objects().filter(age__gte=18, name__startswith="A").exec()
|
|
278
|
+
|
|
279
|
+
# Supported lookups
|
|
280
|
+
# exact, gt, gte, lt, lte, in, like, ilike, contains, icontains,
|
|
281
|
+
# startswith, istartswith, endswith, iendswith, match, regex, isnull
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### ORM Transactions
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
from surreal_orm import SurrealDBConnectionManager
|
|
288
|
+
|
|
289
|
+
# Via ConnectionManager
|
|
290
|
+
async with SurrealDBConnectionManager.transaction() as tx:
|
|
291
|
+
user = User(name="Alice", balance=1000)
|
|
292
|
+
await user.save(tx=tx)
|
|
293
|
+
|
|
294
|
+
order = Order(user_id=user.id, total=100)
|
|
295
|
+
await order.save(tx=tx)
|
|
296
|
+
# Auto-commit on success, auto-rollback on exception
|
|
297
|
+
|
|
298
|
+
# Via Model class method
|
|
299
|
+
async with User.transaction() as tx:
|
|
300
|
+
await user1.save(tx=tx)
|
|
301
|
+
await user2.delete(tx=tx)
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Aggregations
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
# Simple aggregations
|
|
308
|
+
total = await User.objects().count()
|
|
309
|
+
total = await User.objects().filter(active=True).count()
|
|
310
|
+
|
|
311
|
+
# Field aggregations
|
|
312
|
+
avg_age = await User.objects().avg("age")
|
|
313
|
+
total = await Order.objects().filter(status="paid").sum("amount")
|
|
314
|
+
min_val = await Product.objects().min("price")
|
|
315
|
+
max_val = await Product.objects().max("price")
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### GROUP BY with Aggregations
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
from surreal_orm import Count, Sum, Avg
|
|
322
|
+
|
|
323
|
+
# Group by single field
|
|
324
|
+
stats = await Order.objects().values("status").annotate(
|
|
325
|
+
count=Count(),
|
|
326
|
+
total=Sum("amount"),
|
|
327
|
+
).exec()
|
|
328
|
+
# Result: [{"status": "paid", "count": 42, "total": 5000}, ...]
|
|
329
|
+
|
|
330
|
+
# Group by multiple fields
|
|
331
|
+
monthly = await Order.objects().values("status", "month").annotate(
|
|
332
|
+
count=Count(),
|
|
333
|
+
).exec()
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Bulk Operations
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
# Bulk create
|
|
340
|
+
users = [User(name=f"User{i}") for i in range(100)]
|
|
341
|
+
created = await User.objects().bulk_create(users)
|
|
342
|
+
|
|
343
|
+
# Atomic bulk create (all-or-nothing)
|
|
344
|
+
created = await User.objects().bulk_create(users, atomic=True)
|
|
345
|
+
|
|
346
|
+
# Bulk update
|
|
347
|
+
updated = await User.objects().filter(status="pending").bulk_update(
|
|
348
|
+
{"status": "active"}
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
# Bulk delete
|
|
352
|
+
deleted = await User.objects().filter(status="deleted").bulk_delete()
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Table Types
|
|
356
|
+
|
|
357
|
+
| Type | Description |
|
|
358
|
+
| -------- | --------------------------- |
|
|
359
|
+
| `NORMAL` | Standard table (default) |
|
|
360
|
+
| `USER` | Auth table with JWT support |
|
|
361
|
+
| `STREAM` | Real-time with CHANGEFEED |
|
|
362
|
+
| `HASH` | Lookup/cache (SCHEMALESS) |
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
from surreal_orm import BaseSurrealModel, SurrealConfigDict
|
|
366
|
+
from surreal_orm.types import TableType
|
|
367
|
+
|
|
368
|
+
class User(BaseSurrealModel):
|
|
369
|
+
model_config = SurrealConfigDict(
|
|
370
|
+
table_type=TableType.USER,
|
|
371
|
+
permissions={"select": "$auth.id = id"},
|
|
372
|
+
)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### JWT Authentication
|
|
376
|
+
|
|
377
|
+
```python
|
|
378
|
+
from surreal_orm.auth import AuthenticatedUserMixin
|
|
379
|
+
from surreal_orm.fields import Encrypted
|
|
380
|
+
|
|
381
|
+
class User(AuthenticatedUserMixin, BaseSurrealModel):
|
|
382
|
+
model_config = SurrealConfigDict(table_type=TableType.USER)
|
|
383
|
+
email: str
|
|
384
|
+
password: Encrypted # Auto-hashed with argon2
|
|
385
|
+
name: str
|
|
386
|
+
|
|
387
|
+
# Signup
|
|
388
|
+
user = await User.signup(email="alice@example.com", password="secret", name="Alice")
|
|
389
|
+
|
|
390
|
+
# Signin
|
|
391
|
+
user, token = await User.signin(email="alice@example.com", password="secret")
|
|
392
|
+
|
|
393
|
+
# Validate token
|
|
394
|
+
user = await User.authenticate_token(token)
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## CLI Commands
|
|
400
|
+
|
|
401
|
+
Requires `pip install surrealdb-orm[cli]`
|
|
402
|
+
|
|
403
|
+
| Command | Description |
|
|
404
|
+
| ------------------- | --------------------------- |
|
|
405
|
+
| `makemigrations` | Generate migration files |
|
|
406
|
+
| `migrate` | Apply schema migrations |
|
|
407
|
+
| `rollback <target>` | Rollback to migration |
|
|
408
|
+
| `status` | Show migration status |
|
|
409
|
+
| `shell` | Interactive SurrealQL shell |
|
|
410
|
+
|
|
411
|
+
```bash
|
|
412
|
+
# Generate and apply migrations
|
|
413
|
+
surreal-orm makemigrations --name initial
|
|
414
|
+
surreal-orm migrate -u http://localhost:8000 -n myns -d mydb
|
|
415
|
+
|
|
416
|
+
# Environment variables supported
|
|
417
|
+
export SURREAL_URL=http://localhost:8000
|
|
418
|
+
export SURREAL_NAMESPACE=myns
|
|
419
|
+
export SURREAL_DATABASE=mydb
|
|
420
|
+
surreal-orm migrate
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
---
|
|
424
|
+
|
|
425
|
+
## Documentation
|
|
426
|
+
|
|
427
|
+
| Document | Description |
|
|
428
|
+
| -------------------------------------- | ------------------------ |
|
|
429
|
+
| [SDK Guide](docs/sdk.md) | Full SDK documentation |
|
|
430
|
+
| [Migration System](docs/migrations.md) | Django-style migrations |
|
|
431
|
+
| [Authentication](docs/auth.md) | JWT authentication guide |
|
|
432
|
+
| [Roadmap](docs/roadmap.md) | Future features planning |
|
|
433
|
+
| [CHANGELOG](CHANGELOG) | Version history |
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Contributing
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
# Clone and install
|
|
441
|
+
git clone https://github.com/EulogySnowfall/SurrealDB-ORM.git
|
|
442
|
+
cd SurrealDB-ORM
|
|
443
|
+
uv sync
|
|
444
|
+
|
|
445
|
+
# Run tests (SurrealDB container managed automatically)
|
|
446
|
+
make test # Unit tests only
|
|
447
|
+
make test-integration # With integration tests
|
|
448
|
+
|
|
449
|
+
# Start SurrealDB manually
|
|
450
|
+
make db-up # Test instance (port 8001)
|
|
451
|
+
make db-dev # Dev instance (port 8000)
|
|
452
|
+
|
|
453
|
+
# Lint
|
|
454
|
+
make ci-lint # Run all linters
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## License
|
|
460
|
+
|
|
461
|
+
MIT License - See [LICENSE](LICENSE) file.
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
**Author:** Yannick Croteau | **GitHub:** [EulogySnowfall](https://github.com/EulogySnowfall)
|