django-libsql-backend 0.1.0__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.
@@ -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,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,287 @@
1
+ # django-libsql-backend
2
+
3
+ Django database backend for **[libSQL](https://libsql.org/)** and **[Turso](https://turso.tech/)** — supports both remote Turso databases over HTTP and local SQLite files.
4
+
5
+ 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.
6
+
7
+ ---
8
+
9
+ ## Features
10
+
11
+ - **Dual-mode** — auto-detects local file paths vs remote URLs from the `NAME` setting
12
+ - **100% remote when you need it** — no local SQLite files, no file locks, no persistent connections
13
+ - **Full local when you need it** — real transactions, WAL mode, FK enforcement
14
+ - **SQLite-compatible** — delegates to Django's built-in SQLite schema editor and introspection
15
+ - **Migrations** — `makemigrations`, `migrate`, `sqlmigrate` work as expected
16
+ - **ORM** — full queryset API, aggregations, annotations, `ON CONFLICT` (upsert)
17
+ - **Admin** — Django admin works out of the box
18
+ - **Authentication** — password hashing, sessions, permissions
19
+ - **Type mapping** — Django model fields map to correct SQLite column types
20
+ - **Batch API** — `executemany()` uses Turso's `/v1/batch` endpoint for efficient bulk inserts
21
+
22
+ ---
23
+
24
+ ## Requirements
25
+
26
+ | Dependency | Minimum Version |
27
+ |---|---|
28
+ | Python | 3.10+ |
29
+ | Django | 4.2+ (tested on 5.0, 5.1, 6.0) |
30
+ | Turso / libSQL database | SQLite 3.31+ (Turso currently ships 3.45) |
31
+
32
+ The backend uses only Python's standard library (`urllib`, `json`) — **no additional Python packages are required**.
33
+
34
+ ---
35
+
36
+ ## Installation
37
+
38
+ ```bash
39
+ pip install django-libsql-backend
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Quick Start
45
+
46
+ ### 1. Get a Turso database
47
+
48
+ [Create a database](https://docs.turso.tech/sdk/ts/quickstart#step-1-create-a-database) and grab the HTTP URL and an auth token:
49
+
50
+ ```bash
51
+ turso db create my-django-app
52
+ turso db tokens create my-django-app
53
+ ```
54
+
55
+ ### 2. Configure Django
56
+
57
+ In `settings.py`:
58
+
59
+ ```python
60
+ DATABASES = {
61
+ "default": {
62
+ "ENGINE": "django_libsql",
63
+ "NAME": "https://my-django-app-org.turso.io",
64
+ "AUTH_TOKEN": "your-jwt-auth-token-here",
65
+ "OPTIONS": {
66
+ "timeout": 30,
67
+ },
68
+ }
69
+ }
70
+ ```
71
+
72
+ `NAME` accepts any of these forms:
73
+
74
+ | Form | Example | Connection type |
75
+ |---|---|---|
76
+ | Full HTTPS URL | `https://my-db-org.turso.io` | Remote (Turso HTTP) |
77
+ | Bare hostname | `my-db-org.turso.io` | Remote (Turso HTTP) |
78
+ | `libsql://` URL | `libsql://my-db-org.turso.io` | Remote (converted to HTTPS) |
79
+ | Absolute file path | `/var/data/db.sqlite3` | Local (sqlite3) |
80
+ | Relative file path | `./local_dev.db` | Local (sqlite3) |
81
+ | Bare filename | `db.sqlite3` | Local (sqlite3) |
82
+ | In-memory | `:memory:` | Local (sqlite3)
83
+
84
+ ### 3. Run migrations
85
+
86
+ ```bash
87
+ python manage.py migrate
88
+ ```
89
+
90
+ That's it. Django is now running against your remote Turso database.
91
+
92
+ ### Local development workflow
93
+
94
+ During local development, just point `NAME` at a file path — no Turso account needed:
95
+
96
+ ```python
97
+ DATABASES = {
98
+ "default": {
99
+ "ENGINE": "django_libsql",
100
+ "NAME": "dev.sqlite3",
101
+ }
102
+ }
103
+ ```
104
+
105
+ 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.
106
+
107
+ ---
108
+
109
+ ## Configuration Reference
110
+
111
+ | Setting | Required | Default | Description |
112
+ |---|---|---|---|
113
+ | `ENGINE` | Yes | — | `"django_libsql"` |
114
+ | `NAME` | Yes | — | Database URL, hostname, or local file path |
115
+ | `AUTH_TOKEN` | Remote only | `""` | Turso platform auth token (JWT) |
116
+ | `OPTIONS.timeout` | No | `30` | HTTP request timeout in seconds (remote only) |
117
+
118
+ ### Environment variables
119
+
120
+ For production, store the auth token in an environment variable rather than hard-coding it:
121
+
122
+ ```python
123
+ import os
124
+
125
+ DATABASES = {
126
+ "default": {
127
+ "ENGINE": "django_libsql",
128
+ "NAME": os.environ["TURSO_DB_URL"],
129
+ "AUTH_TOKEN": os.environ["TURSO_AUTH_TOKEN"],
130
+ }
131
+ }
132
+ ```
133
+
134
+ ---
135
+
136
+ ## Architecture
137
+
138
+ ```
139
+ ┌──────────┐ HTTP POST /v1/execute ┌──────────────┐
140
+ │ Django │ ───────────────────────────────► │ Turso / │
141
+ │ ORM / │ HTTP POST /v1/batch │ libSQL │
142
+ │ Migrations│ ◄─────────────────────────────── │ Server │
143
+ └──────────┘ JSON (typed-value) └──────────────┘
144
+ ```
145
+
146
+ ### How it differs from Django's built-in SQLite backend
147
+
148
+ | | Django SQLite | django-libsql-backend (local) | django-libsql-backend (remote) |
149
+ |---|---|---|---|---|
150
+ | **Transport** | Local file I/O | Local file I/O | HTTP REST API |
151
+ | **Connection** | Persistent `sqlite3.Connection` | Persistent `sqlite3.Connection` | Stateless — each request = new connection |
152
+ | **Transactions** | Real SQLite transactions | Real SQLite transactions | Each statement auto-commits independently |
153
+ | **Savepoints** | Supported | Supported | Not supported |
154
+ | **PRAGMAs** | Persistent per connection | Persistent per connection | Reset every HTTP request |
155
+ | **File locking** | Yes | Yes | None — Turso handles concurrency |
156
+ | **Schema changes** | `_remake_table` | `_remake_table` | Same, each DDL is its own HTTP call |
157
+
158
+ ### Internal module layout
159
+
160
+ ```
161
+ django_libsql/
162
+ ├── __init__.py # Exports DatabaseWrapper, __version__
163
+ ├── base.py # DatabaseWrapper, TursoCursor, TursoHTTPConnection
164
+ ├── features.py # SQLite-compatible feature flags (39 flags)
165
+ ├── operations.py # SQL generation (date/time, upsert, operators)
166
+ ├── schema.py # Proxy to Django's SQLite schema editor
167
+ ├── introspection.py # Proxy to Django's SQLite introspection
168
+ ├── creation.py # Test database create/destroy
169
+ └── client.py # CLI: python manage.py dbshell → turso db shell
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Supported Django Field Types
175
+
176
+ All standard Django model fields are supported with correct SQLite column types:
177
+
178
+ | Django Field | SQLite Column Type |
179
+ |---|---|
180
+ | `AutoField` / `BigAutoField` / `SmallAutoField` | `integer AUTOINCREMENT` |
181
+ | `CharField` / `SlugField` / `FileField` / `FilePathField` | `varchar(N)` |
182
+ | `TextField` | `text` |
183
+ | `IntegerField` | `integer` |
184
+ | `BigIntegerField` | `bigint` |
185
+ | `BooleanField` | `bool` |
186
+ | `FloatField` | `real` |
187
+ | `DecimalField` | `decimal` |
188
+ | `DateField` | `date` |
189
+ | `DateTimeField` | `datetime` |
190
+ | `TimeField` | `time` |
191
+ | `DurationField` | `bigint` |
192
+ | `BinaryField` | `BLOB` |
193
+ | `JSONField` | `text` (with `JSON_VALID` check constraint) |
194
+ | `UUIDField` | `char(32)` |
195
+ | `IPAddressField` | `char(15)` |
196
+ | `GenericIPAddressField` | `char(39)` |
197
+ | `PositiveIntegerField` | `integer unsigned` |
198
+
199
+ ---
200
+
201
+ ## Known Limitations
202
+
203
+ These apply only to **remote** (Turso HTTP) mode. Local mode has full SQLite transaction support.
204
+
205
+ ### Stateless HTTP connection
206
+
207
+ Each Turso HTTP request creates a new SQLite connection. This means:
208
+
209
+ - **`PRAGMA` settings do not persist** between requests. `PRAGMA foreign_keys = OFF` in request A does not affect request B.
210
+ - **`SET` / transaction state** is not shared across calls.
211
+
212
+ ### Transactions
213
+
214
+ `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.
215
+
216
+ If you need multi-statement atomicity, consider:
217
+ - Using Turso's batch API (the backend uses it automatically for `executemany()`)
218
+ - Calling a server-side function that groups statements
219
+
220
+ ### Foreign key constraints
221
+
222
+ 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.
223
+
224
+ ---
225
+
226
+ ## Development & Testing
227
+
228
+ ```bash
229
+ # Clone the repo
230
+ git clone https://github.com/CyberCalculus/django-libsql-backend.git
231
+ cd django-libsql-backend
232
+
233
+ # Install in development mode
234
+ pip install -e .
235
+
236
+ # Run Django checks
237
+ python manage.py check
238
+
239
+ # Run migrations
240
+ python manage.py migrate
241
+ ```
242
+
243
+ ---
244
+
245
+ ## Troubleshooting
246
+
247
+ ### "Turso HTTP 401"
248
+ Your auth token is invalid or expired. Generate a new one:
249
+ ```bash
250
+ turso db tokens create <db-name>
251
+ ```
252
+
253
+ ### "Turso connection error: timed out"
254
+ Check network connectivity to your Turso database URL. Increase the timeout in `OPTIONS` if needed:
255
+ ```python
256
+ "OPTIONS": {"timeout": 60}
257
+ ```
258
+
259
+ ### "RuntimeError: No connection established"
260
+ The HTTP connection hasn't been initialized. This usually means the database settings are misconfigured — verify `NAME` and `AUTH_TOKEN`.
261
+
262
+ ### Migrations fail with "no such table: django_migrations"
263
+ Run `python manage.py migrate` — this table is created by Django's first migration.
264
+
265
+ ---
266
+
267
+ ## License
268
+
269
+ MIT. See [LICENSE](LICENSE) for details.
270
+
271
+ ---
272
+
273
+ ## Related Projects
274
+
275
+ - [Turso](https://turso.tech/) — Managed libSQL platform
276
+ - [libSQL](https://libsql.org/) — Open-source SQLite fork
277
+ - [Django SQLite backend](https://docs.djangoproject.com/en/stable/ref/databases/#sqlite-notes) — Built-in backend this project is modeled on
278
+
279
+ ---
280
+
281
+ ## Contributing
282
+
283
+ Issues and pull requests are welcome. Before opening a PR, please:
284
+
285
+ 1. Run `python manage.py check` against a Turso database
286
+ 2. Verify `python manage.py migrate` runs cleanly
287
+ 3. Test basic ORM operations (create, read, update, delete)
@@ -0,0 +1,28 @@
1
+ """
2
+ Django database backend for libSQL/Turso.
3
+
4
+ Provides a Django database backend that communicates with remote libSQL/SQLite
5
+ databases via Turso's HTTP REST API (Hrana protocol over HTTP).
6
+
7
+ Usage in Django settings.py::
8
+
9
+ DATABASES = {
10
+ "default": {
11
+ "ENGINE": "django_libsql",
12
+ "NAME": "https://your-database.turso.io",
13
+ "AUTH_TOKEN": "your-jwt-auth-token",
14
+ "OPTIONS": {
15
+ "timeout": 30,
16
+ },
17
+ }
18
+ }
19
+
20
+ The ``NAME`` can be a full URL (``https://db-name.turso.io``) or a bare
21
+ hostname (``db-name.turso.io``) — ``https://`` is prepended automatically.
22
+ """
23
+
24
+ __version__ = "0.1.0"
25
+
26
+ from .base import DatabaseWrapper
27
+
28
+ __all__ = ["DatabaseWrapper", "__version__"]