django-libsql-backend 0.1.0__tar.gz → 0.1.2__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.
- django_libsql_backend-0.1.2/PKG-INFO +272 -0
- django_libsql_backend-0.1.2/README.md +238 -0
- {django_libsql_backend-0.1.0 → django_libsql_backend-0.1.2}/django_libsql/__init__.py +8 -4
- django_libsql_backend-0.1.2/django_libsql/base.py +1218 -0
- django_libsql_backend-0.1.2/django_libsql/client.py +34 -0
- django_libsql_backend-0.1.2/django_libsql/creation.py +206 -0
- django_libsql_backend-0.1.2/django_libsql/features.py +165 -0
- django_libsql_backend-0.1.2/django_libsql/functions.py +24 -0
- {django_libsql_backend-0.1.0 → django_libsql_backend-0.1.2}/django_libsql/introspection.py +2 -0
- django_libsql_backend-0.1.2/django_libsql/operations.py +635 -0
- django_libsql_backend-0.1.2/django_libsql/schema.py +56 -0
- django_libsql_backend-0.1.2/django_libsql_backend.egg-info/PKG-INFO +272 -0
- {django_libsql_backend-0.1.0 → django_libsql_backend-0.1.2}/django_libsql_backend.egg-info/SOURCES.txt +1 -0
- {django_libsql_backend-0.1.0 → django_libsql_backend-0.1.2}/pyproject.toml +12 -1
- django_libsql_backend-0.1.0/PKG-INFO +0 -318
- django_libsql_backend-0.1.0/README.md +0 -287
- django_libsql_backend-0.1.0/django_libsql/base.py +0 -528
- django_libsql_backend-0.1.0/django_libsql/client.py +0 -20
- django_libsql_backend-0.1.0/django_libsql/creation.py +0 -39
- django_libsql_backend-0.1.0/django_libsql/features.py +0 -81
- django_libsql_backend-0.1.0/django_libsql/operations.py +0 -341
- django_libsql_backend-0.1.0/django_libsql/schema.py +0 -42
- django_libsql_backend-0.1.0/django_libsql_backend.egg-info/PKG-INFO +0 -318
- {django_libsql_backend-0.1.0 → django_libsql_backend-0.1.2}/LICENSE +0 -0
- {django_libsql_backend-0.1.0 → django_libsql_backend-0.1.2}/django_libsql_backend.egg-info/dependency_links.txt +0 -0
- {django_libsql_backend-0.1.0 → django_libsql_backend-0.1.2}/django_libsql_backend.egg-info/requires.txt +0 -0
- {django_libsql_backend-0.1.0 → django_libsql_backend-0.1.2}/django_libsql_backend.egg-info/top_level.txt +0 -0
- {django_libsql_backend-0.1.0 → django_libsql_backend-0.1.2}/setup.cfg +0 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-libsql-backend
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Django database backend for libSQL/Turso — SQLite-compatible remote databases over HTTP
|
|
5
|
+
Author-email: CyberCalculus <cybercalculus@example.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/CyberCalculus/django-libsql-backend
|
|
8
|
+
Project-URL: Repository, https://github.com/CyberCalculus/django-libsql-backend
|
|
9
|
+
Project-URL: Issues, https://github.com/CyberCalculus/django-libsql-backend/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/CyberCalculus/django-libsql-backend/tree/main/docs
|
|
11
|
+
Project-URL: Changelog, https://github.com/CyberCalculus/django-libsql-backend/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: django,libsql,turso,sqlite,database,backend,remote,http
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Framework :: Django
|
|
15
|
+
Classifier: Framework :: Django :: 4.2
|
|
16
|
+
Classifier: Framework :: Django :: 5.0
|
|
17
|
+
Classifier: Framework :: Django :: 5.1
|
|
18
|
+
Classifier: Framework :: Django :: 6.0
|
|
19
|
+
Classifier: Intended Audience :: Developers
|
|
20
|
+
Classifier: Operating System :: OS Independent
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Topic :: Database
|
|
27
|
+
Classifier: Topic :: Database :: Database Engines/Servers
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
29
|
+
Requires-Python: >=3.10
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: Django>=4.2
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# django-libsql-backend
|
|
36
|
+
|
|
37
|
+
[](https://pypi.org/project/django-libsql-backend/)
|
|
38
|
+
[](https://pypi.org/project/django-libsql-backend/)
|
|
39
|
+
[](https://pypi.org/project/django-libsql-backend/)
|
|
40
|
+
[](LICENSE)
|
|
41
|
+
|
|
42
|
+
A drop-in Django database backend for **[libSQL](https://libsql.org/)** and **[Turso](https://turso.tech/)**. Supports remote Turso databases over HTTP/WebSocket and local SQLite files.
|
|
43
|
+
|
|
44
|
+
Use a local `.sqlite3` file during development, then switch to a production Turso URL with zero code changes — same `ENGINE`, same ORM, same migrations.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- **Triple-mode** — auto-detects local file paths, remote HTTP URLs, or WebSocket (Hrana) from the `NAME` setting
|
|
51
|
+
- **Zero external dependencies** — uses only Python's stdlib (`urllib`, `json`)
|
|
52
|
+
- **Full ORM support** — querysets, aggregations, annotations, `ON CONFLICT` (upsert)
|
|
53
|
+
- **Migrations** — `makemigrations`, `migrate`, `sqlmigrate` work out of the box
|
|
54
|
+
- **Admin & Auth** — Django admin, password hashing, sessions, permissions
|
|
55
|
+
- **Batch API** — `executemany()` uses Turso's `/v1/batch` endpoint for efficient bulk operations
|
|
56
|
+
- **SQLite-compatible** — delegates to Django's built-in SQLite schema editor and introspection
|
|
57
|
+
- **Django 4.2+** — tested on 5.0, 5.1, and 6.0
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Requirements
|
|
62
|
+
|
|
63
|
+
| Dependency | Minimum |
|
|
64
|
+
|---|---|
|
|
65
|
+
| Python | 3.10+ |
|
|
66
|
+
| Django | 4.2+ |
|
|
67
|
+
| Turso / libSQL | SQLite 3.31+ |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Installation
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install django-libsql-backend
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For WebSocket/Hrana mode:
|
|
78
|
+
```bash
|
|
79
|
+
pip install django-libsql-backend[hrana]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Quick Start
|
|
85
|
+
|
|
86
|
+
### 1. Get a Turso database
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
turso db create my-django-app
|
|
90
|
+
turso db tokens create my-django-app
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 2. Configure Django
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import os
|
|
97
|
+
|
|
98
|
+
DATABASES = {
|
|
99
|
+
"default": {
|
|
100
|
+
"ENGINE": "django_libsql",
|
|
101
|
+
"NAME": os.environ["TURSO_DB_URL"],
|
|
102
|
+
"AUTH_TOKEN": os.environ["TURSO_AUTH_TOKEN"],
|
|
103
|
+
"OPTIONS": {
|
|
104
|
+
"timeout": 30,
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 3. Run migrations
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
python manage.py migrate
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Local development
|
|
117
|
+
|
|
118
|
+
Point `NAME` at a file path — no Turso account needed:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
DATABASES = {
|
|
122
|
+
"default": {
|
|
123
|
+
"ENGINE": "django_libsql",
|
|
124
|
+
"NAME": "dev.sqlite3",
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
When ready to deploy, change `NAME` to your Turso URL and add `AUTH_TOKEN`. Zero code changes required.
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Configuration Reference
|
|
134
|
+
|
|
135
|
+
| Setting | Required | Default | Description |
|
|
136
|
+
|---|---|---|---|
|
|
137
|
+
| `ENGINE` | Yes | — | `"django_libsql"` |
|
|
138
|
+
| `NAME` | Yes | — | Database URL, hostname, or local file path |
|
|
139
|
+
| `AUTH_TOKEN` | Remote only | `""` | Turso platform auth token (JWT) |
|
|
140
|
+
| `OPTIONS.timeout` | No | `30` | HTTP request timeout in seconds |
|
|
141
|
+
| `OPTIONS.transaction_mode` | No | `None` | SQLite transaction mode: `"DEFERRED"`, `"EXCLUSIVE"`, or `"IMMEDIATE"` (local only) |
|
|
142
|
+
| `OPTIONS.init_command` | No | `""` | SQL commands to run on connection (local only, semicolon-separated) |
|
|
143
|
+
|
|
144
|
+
### NAME value formats
|
|
145
|
+
|
|
146
|
+
| Format | Example | Mode |
|
|
147
|
+
|---|---|---|
|
|
148
|
+
| Full HTTPS URL | `https://my-db-org.turso.io` | Remote (HTTP) |
|
|
149
|
+
| Bare hostname | `my-db-org.turso.io` | Remote (HTTP) |
|
|
150
|
+
| `libsql://` URL | `libsql://my-db-org.turso.io` | Remote (HTTP, converted to HTTPS) |
|
|
151
|
+
| `ws://`/`wss://` URL | `wss://my-db-org.turso.io` | Remote (Hrana/WebSocket) |
|
|
152
|
+
| Absolute path | `/var/data/db.sqlite3` | Local |
|
|
153
|
+
| Relative path | `./dev.db` | Local |
|
|
154
|
+
| In-memory | `:memory:` | Local |
|
|
155
|
+
|
|
156
|
+
> **Note:** `libsql://` URLs are automatically converted to HTTPS for the Turso REST API. Use `ws://` or `wss://` explicitly for WebSocket/Hrana mode (requires `pip install django-libsql-backend[hrana]`).
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Architecture
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
┌──────────┐ HTTP POST /v1/execute ┌──────────────┐
|
|
164
|
+
│ Django │ ───────────────────────────────► │ Turso / │
|
|
165
|
+
│ ORM / │ HTTP POST /v1/batch │ libSQL │
|
|
166
|
+
│ Migrations│ ◄─────────────────────────────── │ Server │
|
|
167
|
+
└──────────┘ JSON (typed-value) └──────────────┘
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### How it differs from Django's built-in SQLite backend
|
|
171
|
+
|
|
172
|
+
| | Django SQLite | django-libsql (local) | django-libsql (remote) |
|
|
173
|
+
|---|---|---|---|
|
|
174
|
+
| **Transport** | File I/O | File I/O | HTTP REST API |
|
|
175
|
+
| **Connection** | Persistent | Persistent | Stateless — each request = new connection |
|
|
176
|
+
| **Transactions** | Real | Real | Buffered writes flushed as batch on commit |
|
|
177
|
+
| **Savepoints** | Yes | Yes | Client-side buffer snapshots only |
|
|
178
|
+
| **DDL rollback** | Yes | Yes | Not supported — each DDL auto-commits |
|
|
179
|
+
| **PRAGMAs** | Persistent | Persistent | Reset every HTTP request |
|
|
180
|
+
|
|
181
|
+
### Internal module layout
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
django_libsql/
|
|
185
|
+
├── __init__.py # Exports DatabaseWrapper, __version__
|
|
186
|
+
├── base.py # DatabaseWrapper, TursoCursor, TursoHTTPConnection, HranaCursor
|
|
187
|
+
├── features.py # SQLite-compatible feature flags
|
|
188
|
+
├── operations.py # SQL generation (date/time, upsert, operators)
|
|
189
|
+
├── schema.py # Proxy to Django's SQLite schema editor
|
|
190
|
+
├── introspection.py # Proxy to Django's SQLite introspection
|
|
191
|
+
├── creation.py # Test database create/destroy
|
|
192
|
+
├── client.py # CLI: dbshell (turso or sqlite3)
|
|
193
|
+
└── functions.py # Custom DB functions (local mode)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Known Limitations
|
|
199
|
+
|
|
200
|
+
These apply only to **remote** (Turso HTTP) mode. Local mode has full SQLite support.
|
|
201
|
+
|
|
202
|
+
### Stateless HTTP connection
|
|
203
|
+
|
|
204
|
+
Each Turso HTTP request creates a new SQLite connection. PRAGMA settings do not persist between requests. DDL cannot be rolled back.
|
|
205
|
+
|
|
206
|
+
### Transactions
|
|
207
|
+
|
|
208
|
+
Remote mode uses **client-side write buffering** for best-effort atomicity:
|
|
209
|
+
|
|
210
|
+
- Writes inside `atomic()` blocks are buffered in memory
|
|
211
|
+
- On commit, all buffered writes are flushed as a single batch
|
|
212
|
+
- Reading inside a transaction auto-flushes buffered writes first (read-your-writes)
|
|
213
|
+
- Accessing `cursor.lastrowid` also triggers a flush
|
|
214
|
+
- Once flushed, writes cannot be rolled back
|
|
215
|
+
|
|
216
|
+
### Unavailable SQL functions
|
|
217
|
+
|
|
218
|
+
These Django ORM functions use Python-registered SQL functions not available on remote servers:
|
|
219
|
+
|
|
220
|
+
- **Hash**: `MD5`, `SHA1`, `SHA224`, `SHA256`, `SHA384`, `SHA512`
|
|
221
|
+
- **String**: `LPad`, `RPad`, `Repeat`, `Reverse`
|
|
222
|
+
- **Math**: `Cot`, `Sign` (rewritten automatically), `BitXor` (raises error)
|
|
223
|
+
- **Aggregate**: `StdDev`, `Variance`
|
|
224
|
+
|
|
225
|
+
### Timezone handling
|
|
226
|
+
|
|
227
|
+
SQLite has no native timezone support. Results are correct when the database stores UTC (Django's default).
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Troubleshooting
|
|
232
|
+
|
|
233
|
+
### "Turso HTTP 401"
|
|
234
|
+
Your auth token is invalid or expired. Generate a new one:
|
|
235
|
+
```bash
|
|
236
|
+
turso db tokens create <db-name>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### "Turso connection error: timed out"
|
|
240
|
+
Increase the timeout:
|
|
241
|
+
```python
|
|
242
|
+
"OPTIONS": {"timeout": 60}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### "RuntimeError: No connection established"
|
|
246
|
+
Verify `NAME` and `AUTH_TOKEN` in your database settings.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Development
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
git clone https://github.com/CyberCalculus/django-libsql-backend.git
|
|
254
|
+
cd django-libsql-backend
|
|
255
|
+
pip install -e .
|
|
256
|
+
python manage.py check
|
|
257
|
+
python manage.py migrate
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## License
|
|
263
|
+
|
|
264
|
+
MIT. See [LICENSE](LICENSE).
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Related
|
|
269
|
+
|
|
270
|
+
- [Turso](https://turso.tech/) — Managed libSQL platform
|
|
271
|
+
- [libSQL](https://libsql.org/) — Open-source SQLite fork
|
|
272
|
+
- [Django SQLite backend](https://docs.djangoproject.com/en/stable/ref/databases/#sqlite-notes)
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# django-libsql-backend
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/django-libsql-backend/)
|
|
4
|
+
[](https://pypi.org/project/django-libsql-backend/)
|
|
5
|
+
[](https://pypi.org/project/django-libsql-backend/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
A drop-in Django database backend for **[libSQL](https://libsql.org/)** and **[Turso](https://turso.tech/)**. Supports remote Turso databases over HTTP/WebSocket and local SQLite files.
|
|
9
|
+
|
|
10
|
+
Use a local `.sqlite3` file during development, then switch to a production Turso URL with zero code changes — same `ENGINE`, same ORM, same migrations.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Features
|
|
15
|
+
|
|
16
|
+
- **Triple-mode** — auto-detects local file paths, remote HTTP URLs, or WebSocket (Hrana) from the `NAME` setting
|
|
17
|
+
- **Zero external dependencies** — uses only Python's stdlib (`urllib`, `json`)
|
|
18
|
+
- **Full ORM support** — querysets, aggregations, annotations, `ON CONFLICT` (upsert)
|
|
19
|
+
- **Migrations** — `makemigrations`, `migrate`, `sqlmigrate` work out of the box
|
|
20
|
+
- **Admin & Auth** — Django admin, password hashing, sessions, permissions
|
|
21
|
+
- **Batch API** — `executemany()` uses Turso's `/v1/batch` endpoint for efficient bulk operations
|
|
22
|
+
- **SQLite-compatible** — delegates to Django's built-in SQLite schema editor and introspection
|
|
23
|
+
- **Django 4.2+** — tested on 5.0, 5.1, and 6.0
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Requirements
|
|
28
|
+
|
|
29
|
+
| Dependency | Minimum |
|
|
30
|
+
|---|---|
|
|
31
|
+
| Python | 3.10+ |
|
|
32
|
+
| Django | 4.2+ |
|
|
33
|
+
| Turso / libSQL | SQLite 3.31+ |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install django-libsql-backend
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For WebSocket/Hrana mode:
|
|
44
|
+
```bash
|
|
45
|
+
pip install django-libsql-backend[hrana]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
### 1. Get a Turso database
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
turso db create my-django-app
|
|
56
|
+
turso db tokens create my-django-app
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 2. Configure Django
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import os
|
|
63
|
+
|
|
64
|
+
DATABASES = {
|
|
65
|
+
"default": {
|
|
66
|
+
"ENGINE": "django_libsql",
|
|
67
|
+
"NAME": os.environ["TURSO_DB_URL"],
|
|
68
|
+
"AUTH_TOKEN": os.environ["TURSO_AUTH_TOKEN"],
|
|
69
|
+
"OPTIONS": {
|
|
70
|
+
"timeout": 30,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3. Run migrations
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
python manage.py migrate
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Local development
|
|
83
|
+
|
|
84
|
+
Point `NAME` at a file path — no Turso account needed:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
DATABASES = {
|
|
88
|
+
"default": {
|
|
89
|
+
"ENGINE": "django_libsql",
|
|
90
|
+
"NAME": "dev.sqlite3",
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
When ready to deploy, change `NAME` to your Turso URL and add `AUTH_TOKEN`. Zero code changes required.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Configuration Reference
|
|
100
|
+
|
|
101
|
+
| Setting | Required | Default | Description |
|
|
102
|
+
|---|---|---|---|
|
|
103
|
+
| `ENGINE` | Yes | — | `"django_libsql"` |
|
|
104
|
+
| `NAME` | Yes | — | Database URL, hostname, or local file path |
|
|
105
|
+
| `AUTH_TOKEN` | Remote only | `""` | Turso platform auth token (JWT) |
|
|
106
|
+
| `OPTIONS.timeout` | No | `30` | HTTP request timeout in seconds |
|
|
107
|
+
| `OPTIONS.transaction_mode` | No | `None` | SQLite transaction mode: `"DEFERRED"`, `"EXCLUSIVE"`, or `"IMMEDIATE"` (local only) |
|
|
108
|
+
| `OPTIONS.init_command` | No | `""` | SQL commands to run on connection (local only, semicolon-separated) |
|
|
109
|
+
|
|
110
|
+
### NAME value formats
|
|
111
|
+
|
|
112
|
+
| Format | Example | Mode |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| Full HTTPS URL | `https://my-db-org.turso.io` | Remote (HTTP) |
|
|
115
|
+
| Bare hostname | `my-db-org.turso.io` | Remote (HTTP) |
|
|
116
|
+
| `libsql://` URL | `libsql://my-db-org.turso.io` | Remote (HTTP, converted to HTTPS) |
|
|
117
|
+
| `ws://`/`wss://` URL | `wss://my-db-org.turso.io` | Remote (Hrana/WebSocket) |
|
|
118
|
+
| Absolute path | `/var/data/db.sqlite3` | Local |
|
|
119
|
+
| Relative path | `./dev.db` | Local |
|
|
120
|
+
| In-memory | `:memory:` | Local |
|
|
121
|
+
|
|
122
|
+
> **Note:** `libsql://` URLs are automatically converted to HTTPS for the Turso REST API. Use `ws://` or `wss://` explicitly for WebSocket/Hrana mode (requires `pip install django-libsql-backend[hrana]`).
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Architecture
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
┌──────────┐ HTTP POST /v1/execute ┌──────────────┐
|
|
130
|
+
│ Django │ ───────────────────────────────► │ Turso / │
|
|
131
|
+
│ ORM / │ HTTP POST /v1/batch │ libSQL │
|
|
132
|
+
│ Migrations│ ◄─────────────────────────────── │ Server │
|
|
133
|
+
└──────────┘ JSON (typed-value) └──────────────┘
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### How it differs from Django's built-in SQLite backend
|
|
137
|
+
|
|
138
|
+
| | Django SQLite | django-libsql (local) | django-libsql (remote) |
|
|
139
|
+
|---|---|---|---|
|
|
140
|
+
| **Transport** | File I/O | File I/O | HTTP REST API |
|
|
141
|
+
| **Connection** | Persistent | Persistent | Stateless — each request = new connection |
|
|
142
|
+
| **Transactions** | Real | Real | Buffered writes flushed as batch on commit |
|
|
143
|
+
| **Savepoints** | Yes | Yes | Client-side buffer snapshots only |
|
|
144
|
+
| **DDL rollback** | Yes | Yes | Not supported — each DDL auto-commits |
|
|
145
|
+
| **PRAGMAs** | Persistent | Persistent | Reset every HTTP request |
|
|
146
|
+
|
|
147
|
+
### Internal module layout
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
django_libsql/
|
|
151
|
+
├── __init__.py # Exports DatabaseWrapper, __version__
|
|
152
|
+
├── base.py # DatabaseWrapper, TursoCursor, TursoHTTPConnection, HranaCursor
|
|
153
|
+
├── features.py # SQLite-compatible feature flags
|
|
154
|
+
├── operations.py # SQL generation (date/time, upsert, operators)
|
|
155
|
+
├── schema.py # Proxy to Django's SQLite schema editor
|
|
156
|
+
├── introspection.py # Proxy to Django's SQLite introspection
|
|
157
|
+
├── creation.py # Test database create/destroy
|
|
158
|
+
├── client.py # CLI: dbshell (turso or sqlite3)
|
|
159
|
+
└── functions.py # Custom DB functions (local mode)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Known Limitations
|
|
165
|
+
|
|
166
|
+
These apply only to **remote** (Turso HTTP) mode. Local mode has full SQLite support.
|
|
167
|
+
|
|
168
|
+
### Stateless HTTP connection
|
|
169
|
+
|
|
170
|
+
Each Turso HTTP request creates a new SQLite connection. PRAGMA settings do not persist between requests. DDL cannot be rolled back.
|
|
171
|
+
|
|
172
|
+
### Transactions
|
|
173
|
+
|
|
174
|
+
Remote mode uses **client-side write buffering** for best-effort atomicity:
|
|
175
|
+
|
|
176
|
+
- Writes inside `atomic()` blocks are buffered in memory
|
|
177
|
+
- On commit, all buffered writes are flushed as a single batch
|
|
178
|
+
- Reading inside a transaction auto-flushes buffered writes first (read-your-writes)
|
|
179
|
+
- Accessing `cursor.lastrowid` also triggers a flush
|
|
180
|
+
- Once flushed, writes cannot be rolled back
|
|
181
|
+
|
|
182
|
+
### Unavailable SQL functions
|
|
183
|
+
|
|
184
|
+
These Django ORM functions use Python-registered SQL functions not available on remote servers:
|
|
185
|
+
|
|
186
|
+
- **Hash**: `MD5`, `SHA1`, `SHA224`, `SHA256`, `SHA384`, `SHA512`
|
|
187
|
+
- **String**: `LPad`, `RPad`, `Repeat`, `Reverse`
|
|
188
|
+
- **Math**: `Cot`, `Sign` (rewritten automatically), `BitXor` (raises error)
|
|
189
|
+
- **Aggregate**: `StdDev`, `Variance`
|
|
190
|
+
|
|
191
|
+
### Timezone handling
|
|
192
|
+
|
|
193
|
+
SQLite has no native timezone support. Results are correct when the database stores UTC (Django's default).
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Troubleshooting
|
|
198
|
+
|
|
199
|
+
### "Turso HTTP 401"
|
|
200
|
+
Your auth token is invalid or expired. Generate a new one:
|
|
201
|
+
```bash
|
|
202
|
+
turso db tokens create <db-name>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### "Turso connection error: timed out"
|
|
206
|
+
Increase the timeout:
|
|
207
|
+
```python
|
|
208
|
+
"OPTIONS": {"timeout": 60}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### "RuntimeError: No connection established"
|
|
212
|
+
Verify `NAME` and `AUTH_TOKEN` in your database settings.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
git clone https://github.com/CyberCalculus/django-libsql-backend.git
|
|
220
|
+
cd django-libsql-backend
|
|
221
|
+
pip install -e .
|
|
222
|
+
python manage.py check
|
|
223
|
+
python manage.py migrate
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT. See [LICENSE](LICENSE).
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Related
|
|
235
|
+
|
|
236
|
+
- [Turso](https://turso.tech/) — Managed libSQL platform
|
|
237
|
+
- [libSQL](https://libsql.org/) — Open-source SQLite fork
|
|
238
|
+
- [Django SQLite backend](https://docs.djangoproject.com/en/stable/ref/databases/#sqlite-notes)
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
Django database backend for libSQL/Turso.
|
|
3
3
|
|
|
4
4
|
Provides a Django database backend that communicates with remote libSQL/SQLite
|
|
5
|
-
databases via Turso's HTTP REST API (Hrana protocol over HTTP)
|
|
5
|
+
databases via Turso's HTTP REST API (Hrana protocol over HTTP) or WebSocket
|
|
6
|
+
(Hrana protocol), or uses local SQLite files.
|
|
6
7
|
|
|
7
8
|
Usage in Django settings.py::
|
|
8
9
|
|
|
@@ -17,11 +18,14 @@ Usage in Django settings.py::
|
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
The ``NAME`` can be a full URL (``https://db-name.turso.io``)
|
|
21
|
-
hostname (``db-name.turso.io``)
|
|
21
|
+
The ``NAME`` can be a full URL (``https://db-name.turso.io``), a bare
|
|
22
|
+
hostname (``db-name.turso.io``), a ``libsql://`` WebSocket URL, or a local
|
|
23
|
+
file path.
|
|
22
24
|
"""
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
__version__ = "0.1.2"
|
|
25
29
|
|
|
26
30
|
from .base import DatabaseWrapper
|
|
27
31
|
|