django-libsql-backend 0.1.0__py3-none-any.whl
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/__init__.py +28 -0
- django_libsql/base.py +528 -0
- django_libsql/client.py +20 -0
- django_libsql/creation.py +39 -0
- django_libsql/features.py +81 -0
- django_libsql/introspection.py +20 -0
- django_libsql/operations.py +341 -0
- django_libsql/schema.py +42 -0
- django_libsql_backend-0.1.0.dist-info/METADATA +318 -0
- django_libsql_backend-0.1.0.dist-info/RECORD +13 -0
- django_libsql_backend-0.1.0.dist-info/WHEEL +5 -0
- django_libsql_backend-0.1.0.dist-info/licenses/LICENSE +21 -0
- django_libsql_backend-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-libsql-backend
|
|
3
|
+
Version: 0.1.0
|
|
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
|
+
Keywords: django,libsql,turso,sqlite,database,backend,remote,http
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Framework :: Django
|
|
13
|
+
Classifier: Framework :: Django :: 5.0
|
|
14
|
+
Classifier: Framework :: Django :: 5.1
|
|
15
|
+
Classifier: Framework :: Django :: 6.0
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Database
|
|
24
|
+
Classifier: Topic :: Database :: Database Engines/Servers
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: Django>=4.2
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# django-libsql-backend
|
|
33
|
+
|
|
34
|
+
Django database backend for **[libSQL](https://libsql.org/)** and **[Turso](https://turso.tech/)** — supports both remote Turso databases over HTTP and local SQLite files.
|
|
35
|
+
|
|
36
|
+
Drop-in replacement for Django's built-in SQLite backend. Use a local `.sqlite3` file during development, then switch to a production Turso URL with zero code changes — same ENGINE, same ORM, same migrations.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Dual-mode** — auto-detects local file paths vs remote URLs from the `NAME` setting
|
|
43
|
+
- **100% remote when you need it** — no local SQLite files, no file locks, no persistent connections
|
|
44
|
+
- **Full local when you need it** — real transactions, WAL mode, FK enforcement
|
|
45
|
+
- **SQLite-compatible** — delegates to Django's built-in SQLite schema editor and introspection
|
|
46
|
+
- **Migrations** — `makemigrations`, `migrate`, `sqlmigrate` work as expected
|
|
47
|
+
- **ORM** — full queryset API, aggregations, annotations, `ON CONFLICT` (upsert)
|
|
48
|
+
- **Admin** — Django admin works out of the box
|
|
49
|
+
- **Authentication** — password hashing, sessions, permissions
|
|
50
|
+
- **Type mapping** — Django model fields map to correct SQLite column types
|
|
51
|
+
- **Batch API** — `executemany()` uses Turso's `/v1/batch` endpoint for efficient bulk inserts
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Requirements
|
|
56
|
+
|
|
57
|
+
| Dependency | Minimum Version |
|
|
58
|
+
|---|---|
|
|
59
|
+
| Python | 3.10+ |
|
|
60
|
+
| Django | 4.2+ (tested on 5.0, 5.1, 6.0) |
|
|
61
|
+
| Turso / libSQL database | SQLite 3.31+ (Turso currently ships 3.45) |
|
|
62
|
+
|
|
63
|
+
The backend uses only Python's standard library (`urllib`, `json`) — **no additional Python packages are required**.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Installation
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install django-libsql-backend
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Quick Start
|
|
76
|
+
|
|
77
|
+
### 1. Get a Turso database
|
|
78
|
+
|
|
79
|
+
[Create a database](https://docs.turso.tech/sdk/ts/quickstart#step-1-create-a-database) and grab the HTTP URL and an auth token:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
turso db create my-django-app
|
|
83
|
+
turso db tokens create my-django-app
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 2. Configure Django
|
|
87
|
+
|
|
88
|
+
In `settings.py`:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
DATABASES = {
|
|
92
|
+
"default": {
|
|
93
|
+
"ENGINE": "django_libsql",
|
|
94
|
+
"NAME": "https://my-django-app-org.turso.io",
|
|
95
|
+
"AUTH_TOKEN": "your-jwt-auth-token-here",
|
|
96
|
+
"OPTIONS": {
|
|
97
|
+
"timeout": 30,
|
|
98
|
+
},
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`NAME` accepts any of these forms:
|
|
104
|
+
|
|
105
|
+
| Form | Example | Connection type |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| Full HTTPS URL | `https://my-db-org.turso.io` | Remote (Turso HTTP) |
|
|
108
|
+
| Bare hostname | `my-db-org.turso.io` | Remote (Turso HTTP) |
|
|
109
|
+
| `libsql://` URL | `libsql://my-db-org.turso.io` | Remote (converted to HTTPS) |
|
|
110
|
+
| Absolute file path | `/var/data/db.sqlite3` | Local (sqlite3) |
|
|
111
|
+
| Relative file path | `./local_dev.db` | Local (sqlite3) |
|
|
112
|
+
| Bare filename | `db.sqlite3` | Local (sqlite3) |
|
|
113
|
+
| In-memory | `:memory:` | Local (sqlite3)
|
|
114
|
+
|
|
115
|
+
### 3. Run migrations
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
python manage.py migrate
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
That's it. Django is now running against your remote Turso database.
|
|
122
|
+
|
|
123
|
+
### Local development workflow
|
|
124
|
+
|
|
125
|
+
During local development, just point `NAME` at a file path — no Turso account needed:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
DATABASES = {
|
|
129
|
+
"default": {
|
|
130
|
+
"ENGINE": "django_libsql",
|
|
131
|
+
"NAME": "dev.sqlite3",
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Run `python manage.py migrate` and you're developing locally with full SQLite transaction support, WAL mode, and FK enforcement. When you're ready to deploy, change `NAME` to your Turso URL and add the `AUTH_TOKEN` — that's the only change needed.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Configuration Reference
|
|
141
|
+
|
|
142
|
+
| Setting | Required | Default | Description |
|
|
143
|
+
|---|---|---|---|
|
|
144
|
+
| `ENGINE` | Yes | — | `"django_libsql"` |
|
|
145
|
+
| `NAME` | Yes | — | Database URL, hostname, or local file path |
|
|
146
|
+
| `AUTH_TOKEN` | Remote only | `""` | Turso platform auth token (JWT) |
|
|
147
|
+
| `OPTIONS.timeout` | No | `30` | HTTP request timeout in seconds (remote only) |
|
|
148
|
+
|
|
149
|
+
### Environment variables
|
|
150
|
+
|
|
151
|
+
For production, store the auth token in an environment variable rather than hard-coding it:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
import os
|
|
155
|
+
|
|
156
|
+
DATABASES = {
|
|
157
|
+
"default": {
|
|
158
|
+
"ENGINE": "django_libsql",
|
|
159
|
+
"NAME": os.environ["TURSO_DB_URL"],
|
|
160
|
+
"AUTH_TOKEN": os.environ["TURSO_AUTH_TOKEN"],
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Architecture
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
┌──────────┐ HTTP POST /v1/execute ┌──────────────┐
|
|
171
|
+
│ Django │ ───────────────────────────────► │ Turso / │
|
|
172
|
+
│ ORM / │ HTTP POST /v1/batch │ libSQL │
|
|
173
|
+
│ Migrations│ ◄─────────────────────────────── │ Server │
|
|
174
|
+
└──────────┘ JSON (typed-value) └──────────────┘
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### How it differs from Django's built-in SQLite backend
|
|
178
|
+
|
|
179
|
+
| | Django SQLite | django-libsql-backend (local) | django-libsql-backend (remote) |
|
|
180
|
+
|---|---|---|---|---|
|
|
181
|
+
| **Transport** | Local file I/O | Local file I/O | HTTP REST API |
|
|
182
|
+
| **Connection** | Persistent `sqlite3.Connection` | Persistent `sqlite3.Connection` | Stateless — each request = new connection |
|
|
183
|
+
| **Transactions** | Real SQLite transactions | Real SQLite transactions | Each statement auto-commits independently |
|
|
184
|
+
| **Savepoints** | Supported | Supported | Not supported |
|
|
185
|
+
| **PRAGMAs** | Persistent per connection | Persistent per connection | Reset every HTTP request |
|
|
186
|
+
| **File locking** | Yes | Yes | None — Turso handles concurrency |
|
|
187
|
+
| **Schema changes** | `_remake_table` | `_remake_table` | Same, each DDL is its own HTTP call |
|
|
188
|
+
|
|
189
|
+
### Internal module layout
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
django_libsql/
|
|
193
|
+
├── __init__.py # Exports DatabaseWrapper, __version__
|
|
194
|
+
├── base.py # DatabaseWrapper, TursoCursor, TursoHTTPConnection
|
|
195
|
+
├── features.py # SQLite-compatible feature flags (39 flags)
|
|
196
|
+
├── operations.py # SQL generation (date/time, upsert, operators)
|
|
197
|
+
├── schema.py # Proxy to Django's SQLite schema editor
|
|
198
|
+
├── introspection.py # Proxy to Django's SQLite introspection
|
|
199
|
+
├── creation.py # Test database create/destroy
|
|
200
|
+
└── client.py # CLI: python manage.py dbshell → turso db shell
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Supported Django Field Types
|
|
206
|
+
|
|
207
|
+
All standard Django model fields are supported with correct SQLite column types:
|
|
208
|
+
|
|
209
|
+
| Django Field | SQLite Column Type |
|
|
210
|
+
|---|---|
|
|
211
|
+
| `AutoField` / `BigAutoField` / `SmallAutoField` | `integer AUTOINCREMENT` |
|
|
212
|
+
| `CharField` / `SlugField` / `FileField` / `FilePathField` | `varchar(N)` |
|
|
213
|
+
| `TextField` | `text` |
|
|
214
|
+
| `IntegerField` | `integer` |
|
|
215
|
+
| `BigIntegerField` | `bigint` |
|
|
216
|
+
| `BooleanField` | `bool` |
|
|
217
|
+
| `FloatField` | `real` |
|
|
218
|
+
| `DecimalField` | `decimal` |
|
|
219
|
+
| `DateField` | `date` |
|
|
220
|
+
| `DateTimeField` | `datetime` |
|
|
221
|
+
| `TimeField` | `time` |
|
|
222
|
+
| `DurationField` | `bigint` |
|
|
223
|
+
| `BinaryField` | `BLOB` |
|
|
224
|
+
| `JSONField` | `text` (with `JSON_VALID` check constraint) |
|
|
225
|
+
| `UUIDField` | `char(32)` |
|
|
226
|
+
| `IPAddressField` | `char(15)` |
|
|
227
|
+
| `GenericIPAddressField` | `char(39)` |
|
|
228
|
+
| `PositiveIntegerField` | `integer unsigned` |
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Known Limitations
|
|
233
|
+
|
|
234
|
+
These apply only to **remote** (Turso HTTP) mode. Local mode has full SQLite transaction support.
|
|
235
|
+
|
|
236
|
+
### Stateless HTTP connection
|
|
237
|
+
|
|
238
|
+
Each Turso HTTP request creates a new SQLite connection. This means:
|
|
239
|
+
|
|
240
|
+
- **`PRAGMA` settings do not persist** between requests. `PRAGMA foreign_keys = OFF` in request A does not affect request B.
|
|
241
|
+
- **`SET` / transaction state** is not shared across calls.
|
|
242
|
+
|
|
243
|
+
### Transactions
|
|
244
|
+
|
|
245
|
+
`commit()`, `rollback()`, `savepoints`, and `atomic()` blocks are **no-ops** — each SQL statement is its own auto-committed transaction over HTTP. For most web applications this is acceptable because Django's default autocommit mode already wraps each request in a single implicit transaction.
|
|
246
|
+
|
|
247
|
+
If you need multi-statement atomicity, consider:
|
|
248
|
+
- Using Turso's batch API (the backend uses it automatically for `executemany()`)
|
|
249
|
+
- Calling a server-side function that groups statements
|
|
250
|
+
|
|
251
|
+
### Foreign key constraints
|
|
252
|
+
|
|
253
|
+
Since FK pragmas don't persist, the schema editor bypasses Django's FK-disabling requirement. This works for **creating new tables** (the common path during initial `migrate`). If you later use `ALTER TABLE` operations that rebuild tables, Turso handles the DDL isolation server-side.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Development & Testing
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Clone the repo
|
|
261
|
+
git clone https://github.com/CyberCalculus/django-libsql-backend.git
|
|
262
|
+
cd django-libsql-backend
|
|
263
|
+
|
|
264
|
+
# Install in development mode
|
|
265
|
+
pip install -e .
|
|
266
|
+
|
|
267
|
+
# Run Django checks
|
|
268
|
+
python manage.py check
|
|
269
|
+
|
|
270
|
+
# Run migrations
|
|
271
|
+
python manage.py migrate
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Troubleshooting
|
|
277
|
+
|
|
278
|
+
### "Turso HTTP 401"
|
|
279
|
+
Your auth token is invalid or expired. Generate a new one:
|
|
280
|
+
```bash
|
|
281
|
+
turso db tokens create <db-name>
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### "Turso connection error: timed out"
|
|
285
|
+
Check network connectivity to your Turso database URL. Increase the timeout in `OPTIONS` if needed:
|
|
286
|
+
```python
|
|
287
|
+
"OPTIONS": {"timeout": 60}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### "RuntimeError: No connection established"
|
|
291
|
+
The HTTP connection hasn't been initialized. This usually means the database settings are misconfigured — verify `NAME` and `AUTH_TOKEN`.
|
|
292
|
+
|
|
293
|
+
### Migrations fail with "no such table: django_migrations"
|
|
294
|
+
Run `python manage.py migrate` — this table is created by Django's first migration.
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## License
|
|
299
|
+
|
|
300
|
+
MIT. See [LICENSE](LICENSE) for details.
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Related Projects
|
|
305
|
+
|
|
306
|
+
- [Turso](https://turso.tech/) — Managed libSQL platform
|
|
307
|
+
- [libSQL](https://libsql.org/) — Open-source SQLite fork
|
|
308
|
+
- [Django SQLite backend](https://docs.djangoproject.com/en/stable/ref/databases/#sqlite-notes) — Built-in backend this project is modeled on
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Contributing
|
|
313
|
+
|
|
314
|
+
Issues and pull requests are welcome. Before opening a PR, please:
|
|
315
|
+
|
|
316
|
+
1. Run `python manage.py check` against a Turso database
|
|
317
|
+
2. Verify `python manage.py migrate` runs cleanly
|
|
318
|
+
3. Test basic ORM operations (create, read, update, delete)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
django_libsql/__init__.py,sha256=0rrk0Z87RWlUr8i0vGxi2LbVh8IQmZrowsliMu30Js4,748
|
|
2
|
+
django_libsql/base.py,sha256=F123L814L5vxDAcYNZx_KBslj2m0OfGICrVjpgpV_sg,17547
|
|
3
|
+
django_libsql/client.py,sha256=QRKwTZb8-oGpI_Ux7cEveaAUiBug8SaZ7RTxJT2tULk,596
|
|
4
|
+
django_libsql/creation.py,sha256=eOwAIlVVRGABYJsJXYwWmWQFag9P32xZji5lPsNTRjs,1447
|
|
5
|
+
django_libsql/features.py,sha256=--Q9J0LuWJpxh0dZ2VCZJ2v1gZ1KVYRXtUn3l1NOd3o,2734
|
|
6
|
+
django_libsql/introspection.py,sha256=kVGOl7N8lnT7H8DfuWzfBM4zxxJWHf89GZ8NixazkVg,625
|
|
7
|
+
django_libsql/operations.py,sha256=rmGwEcBHAv1makz7EAir2cLMPnk0fKbpwQeyPYg3R1s,12642
|
|
8
|
+
django_libsql/schema.py,sha256=oo5anpJ2rt5gwI-tMkf3k0vJG8RM8DQtMHY6TSKFNBw,1745
|
|
9
|
+
django_libsql_backend-0.1.0.dist-info/licenses/LICENSE,sha256=-MuwAAAqsVXwA85xx9fD_tVXJp7rH7KPmbZuKPOSEvM,1070
|
|
10
|
+
django_libsql_backend-0.1.0.dist-info/METADATA,sha256=dtHExfOZynKW2O1kdOxFytISDMYglFwLozxPkBS_KFU,11122
|
|
11
|
+
django_libsql_backend-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
12
|
+
django_libsql_backend-0.1.0.dist-info/top_level.txt,sha256=re-cAEudduEPHk7XYcIYRXdJxosXl--ueWzwZS8XBWM,14
|
|
13
|
+
django_libsql_backend-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CyberCalculus
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
django_libsql
|