Django database backend for libSQL/Turso — SQLite-compatible remote databases over HTTP
Project description
django-libsql-backend
A drop-in Django database backend for libSQL and Turso. Supports remote Turso databases over HTTP/WebSocket and local SQLite files.
Use a local .sqlite3 file during development, then switch to a production Turso URL with zero code changes — same ENGINE, same ORM, same migrations.
Features
- Triple-mode — auto-detects local file paths, remote HTTP URLs, or WebSocket (Hrana) from the
NAMEsetting - Zero external dependencies — uses only Python's stdlib (
urllib,json) - Full ORM support — querysets, aggregations, annotations,
ON CONFLICT(upsert) - Migrations —
makemigrations,migrate,sqlmigratework out of the box - Admin & Auth — Django admin, password hashing, sessions, permissions
- Batch API —
executemany()uses Turso's/v1/batchendpoint for efficient bulk operations - SQLite-compatible — delegates to Django's built-in SQLite schema editor and introspection
- Django 4.2+ — tested on 5.0, 5.1, and 6.0
Requirements
| Dependency | Minimum |
|---|---|
| Python | 3.10+ |
| Django | 4.2+ |
| Turso / libSQL | SQLite 3.31+ |
Installation
pip install django-libsql-backend
For WebSocket/Hrana mode:
pip install django-libsql-backend[hrana]
Quick Start
1. Get a Turso database
turso db create my-django-app
turso db tokens create my-django-app
2. Configure Django
import os
DATABASES = {
"default": {
"ENGINE": "django_libsql",
"NAME": os.environ["TURSO_DB_URL"],
"AUTH_TOKEN": os.environ["TURSO_AUTH_TOKEN"],
"OPTIONS": {
"timeout": 30,
},
}
}
3. Run migrations
python manage.py migrate
Local development
Point NAME at a file path — no Turso account needed:
DATABASES = {
"default": {
"ENGINE": "django_libsql",
"NAME": "dev.sqlite3",
}
}
When ready to deploy, change NAME to your Turso URL and add AUTH_TOKEN. Zero code changes required.
Configuration Reference
| Setting | Required | Default | Description |
|---|---|---|---|
ENGINE |
Yes | — | "django_libsql" |
NAME |
Yes | — | Database URL, hostname, or local file path |
AUTH_TOKEN |
Remote only | "" |
Turso platform auth token (JWT) |
OPTIONS.timeout |
No | 30 |
HTTP request timeout in seconds |
OPTIONS.transaction_mode |
No | None |
SQLite transaction mode: "DEFERRED", "EXCLUSIVE", or "IMMEDIATE" (local only) |
OPTIONS.init_command |
No | "" |
SQL commands to run on connection (local only, semicolon-separated) |
NAME value formats
| Format | Example | Mode |
|---|---|---|
| Full HTTPS URL | https://my-db-org.turso.io |
Remote (HTTP) |
| Bare hostname | my-db-org.turso.io |
Remote (HTTP) |
libsql:// URL |
libsql://my-db-org.turso.io |
Remote (HTTP, converted to HTTPS) |
ws:///wss:// URL |
wss://my-db-org.turso.io |
Remote (Hrana/WebSocket) |
| Absolute path | /var/data/db.sqlite3 |
Local |
| Relative path | ./dev.db |
Local |
| In-memory | :memory: |
Local |
Note:
libsql://URLs are automatically converted to HTTPS for the Turso REST API. Usews://orwss://explicitly for WebSocket/Hrana mode (requirespip install django-libsql-backend[hrana]).
Architecture
┌──────────┐ HTTP POST /v1/execute ┌──────────────┐
│ Django │ ───────────────────────────────► │ Turso / │
│ ORM / │ HTTP POST /v1/batch │ libSQL │
│ Migrations│ ◄─────────────────────────────── │ Server │
└──────────┘ JSON (typed-value) └──────────────┘
How it differs from Django's built-in SQLite backend
| Django SQLite | django-libsql (local) | django-libsql (remote) | |
|---|---|---|---|
| Transport | File I/O | File I/O | HTTP REST API |
| Connection | Persistent | Persistent | Stateless — each request = new connection |
| Transactions | Real | Real | Buffered writes flushed as batch on commit |
| Savepoints | Yes | Yes | Client-side buffer snapshots only |
| DDL rollback | Yes | Yes | Not supported — each DDL auto-commits |
| PRAGMAs | Persistent | Persistent | Reset every HTTP request |
Internal module layout
django_libsql/
├── __init__.py # Exports DatabaseWrapper, __version__
├── base.py # DatabaseWrapper, TursoCursor, TursoHTTPConnection, HranaCursor
├── features.py # SQLite-compatible feature flags
├── operations.py # SQL generation (date/time, upsert, operators)
├── schema.py # Proxy to Django's SQLite schema editor
├── introspection.py # Proxy to Django's SQLite introspection
├── creation.py # Test database create/destroy
├── client.py # CLI: dbshell (turso or sqlite3)
└── functions.py # Custom DB functions (local mode)
Known Limitations
These apply only to remote (Turso HTTP) mode. Local mode has full SQLite support.
Stateless HTTP connection
Each Turso HTTP request creates a new SQLite connection. PRAGMA settings do not persist between requests. DDL cannot be rolled back.
Transactions
Remote mode uses client-side write buffering for best-effort atomicity:
- Writes inside
atomic()blocks are buffered in memory - On commit, all buffered writes are flushed as a single batch
- Reading inside a transaction auto-flushes buffered writes first (read-your-writes)
- Accessing
cursor.lastrowidalso triggers a flush - Once flushed, writes cannot be rolled back
Unavailable SQL functions
These Django ORM functions use Python-registered SQL functions not available on remote servers:
- Hash:
MD5,SHA1,SHA224,SHA256,SHA384,SHA512 - String:
LPad,RPad,Repeat,Reverse - Math:
Cot,Sign(rewritten automatically),BitXor(raises error) - Aggregate:
StdDev,Variance
Timezone handling
SQLite has no native timezone support. Results are correct when the database stores UTC (Django's default).
Troubleshooting
"Turso HTTP 401"
Your auth token is invalid or expired. Generate a new one:
turso db tokens create <db-name>
"Turso connection error: timed out"
Increase the timeout:
"OPTIONS": {"timeout": 60}
"RuntimeError: No connection established"
Verify NAME and AUTH_TOKEN in your database settings.
Development
git clone https://github.com/CyberCalculus/django-libsql-backend.git
cd django-libsql-backend
pip install -e .
python manage.py check
python manage.py migrate
License
MIT. See LICENSE.
Related
- Turso — Managed libSQL platform
- libSQL — Open-source SQLite fork
- Django SQLite backend
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_libsql_backend-0.1.2.tar.gz.
File metadata
- Download URL: django_libsql_backend-0.1.2.tar.gz
- Upload date:
- Size: 30.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0003cd6bef7c94d35f16430b5d98499fabb51f90486fe0b8a44f09b87e28850d
|
|
| MD5 |
7d4093bc214115fd93e16427239aa574
|
|
| BLAKE2b-256 |
1f847c8f7f5070cdd2f2871f3189d33163ddea64066e1d701fbda2e2f7416b33
|
Provenance
The following attestation bundles were made for django_libsql_backend-0.1.2.tar.gz:
Publisher:
publish.yml on CyberCalculus/django-libsql-backend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_libsql_backend-0.1.2.tar.gz -
Subject digest:
0003cd6bef7c94d35f16430b5d98499fabb51f90486fe0b8a44f09b87e28850d - Sigstore transparency entry: 1801132469
- Sigstore integration time:
-
Permalink:
CyberCalculus/django-libsql-backend@c26a7bf75b2cc880c7285806a788ecd84a4520c7 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/CyberCalculus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c26a7bf75b2cc880c7285806a788ecd84a4520c7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file django_libsql_backend-0.1.2-py3-none-any.whl.
File metadata
- Download URL: django_libsql_backend-0.1.2-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dde76666c3d69251276d6e6f83e23d8eb807abc10d809dc4d308f923cf9e7534
|
|
| MD5 |
63558650d7355d6d03de4979557fa69b
|
|
| BLAKE2b-256 |
3b60168e0e12a177da704b0fa20d45f2ad7e5a055638cc178ff3b787c5988b3a
|
Provenance
The following attestation bundles were made for django_libsql_backend-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on CyberCalculus/django-libsql-backend
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_libsql_backend-0.1.2-py3-none-any.whl -
Subject digest:
dde76666c3d69251276d6e6f83e23d8eb807abc10d809dc4d308f923cf9e7534 - Sigstore transparency entry: 1801132932
- Sigstore integration time:
-
Permalink:
CyberCalculus/django-libsql-backend@c26a7bf75b2cc880c7285806a788ecd84a4520c7 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/CyberCalculus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@c26a7bf75b2cc880c7285806a788ecd84a4520c7 -
Trigger Event:
push
-
Statement type: