Skip to main content

Django database backend for libSQL/Turso — SQLite-compatible remote databases over HTTP

Project description

django-libsql-backend

Django database backend for libSQL and Turso — supports both remote Turso databases over HTTP and local SQLite files.

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.


Features

  • Dual-mode — auto-detects local file paths vs remote URLs from the NAME setting
  • 100% remote when you need it — no local SQLite files, no file locks, no persistent connections
  • Full local when you need it — real transactions, WAL mode, FK enforcement
  • SQLite-compatible — delegates to Django's built-in SQLite schema editor and introspection
  • Migrationsmakemigrations, migrate, sqlmigrate work as expected
  • ORM — full queryset API, aggregations, annotations, ON CONFLICT (upsert)
  • Admin — Django admin works out of the box
  • Authentication — password hashing, sessions, permissions
  • Type mapping — Django model fields map to correct SQLite column types
  • Batch APIexecutemany() uses Turso's /v1/batch endpoint for efficient bulk inserts

Requirements

Dependency Minimum Version
Python 3.10+
Django 4.2+ (tested on 5.0, 5.1, 6.0)
Turso / libSQL database SQLite 3.31+ (Turso currently ships 3.45)

The backend uses only Python's standard library (urllib, json) — no additional Python packages are required.


Installation

pip install django-libsql-backend

Quick Start

1. Get a Turso database

Create a database and grab the HTTP URL and an auth token:

turso db create my-django-app
turso db tokens create my-django-app

2. Configure Django

In settings.py:

DATABASES = {
    "default": {
        "ENGINE": "django_libsql",
        "NAME": "https://my-django-app-org.turso.io",
        "AUTH_TOKEN": "your-jwt-auth-token-here",
        "OPTIONS": {
            "timeout": 30,
        },
    }
}

NAME accepts any of these forms:

Form Example Connection type
Full HTTPS URL https://my-db-org.turso.io Remote (Turso HTTP)
Bare hostname my-db-org.turso.io Remote (Turso HTTP)
libsql:// URL libsql://my-db-org.turso.io Remote (converted to HTTPS)
Absolute file path /var/data/db.sqlite3 Local (sqlite3)
Relative file path ./local_dev.db Local (sqlite3)
Bare filename db.sqlite3 Local (sqlite3)
In-memory :memory: Local (sqlite3)

3. Run migrations

python manage.py migrate

That's it. Django is now running against your remote Turso database.

Local development workflow

During local development, just point NAME at a file path — no Turso account needed:

DATABASES = {
    "default": {
        "ENGINE": "django_libsql",
        "NAME": "dev.sqlite3",
    }
}

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.


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 (remote only)

Environment variables

For production, store the auth token in an environment variable rather than hard-coding it:

import os

DATABASES = {
    "default": {
        "ENGINE": "django_libsql",
        "NAME": os.environ["TURSO_DB_URL"],
        "AUTH_TOKEN": os.environ["TURSO_AUTH_TOKEN"],
    }
}

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-backend (local) | django-libsql-backend (remote) | |---|---|---|---|---| | Transport | Local file I/O | Local file I/O | HTTP REST API | | Connection | Persistent sqlite3.Connection | Persistent sqlite3.Connection | Stateless — each request = new connection | | Transactions | Real SQLite transactions | Real SQLite transactions | Each statement auto-commits independently | | Savepoints | Supported | Supported | Not supported | | PRAGMAs | Persistent per connection | Persistent per connection | Reset every HTTP request | | File locking | Yes | Yes | None — Turso handles concurrency | | Schema changes | _remake_table | _remake_table | Same, each DDL is its own HTTP call |

Internal module layout

django_libsql/
├── __init__.py       # Exports DatabaseWrapper, __version__
├── base.py            # DatabaseWrapper, TursoCursor, TursoHTTPConnection
├── features.py        # SQLite-compatible feature flags (39 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: python manage.py dbshell → turso db shell

Supported Django Field Types

All standard Django model fields are supported with correct SQLite column types:

Django Field SQLite Column Type
AutoField / BigAutoField / SmallAutoField integer AUTOINCREMENT
CharField / SlugField / FileField / FilePathField varchar(N)
TextField text
IntegerField integer
BigIntegerField bigint
BooleanField bool
FloatField real
DecimalField decimal
DateField date
DateTimeField datetime
TimeField time
DurationField bigint
BinaryField BLOB
JSONField text (with JSON_VALID check constraint)
UUIDField char(32)
IPAddressField char(15)
GenericIPAddressField char(39)
PositiveIntegerField integer unsigned

Known Limitations

These apply only to remote (Turso HTTP) mode. Local mode has full SQLite transaction support.

Stateless HTTP connection

Each Turso HTTP request creates a new SQLite connection. This means:

  • PRAGMA settings do not persist between requests. PRAGMA foreign_keys = OFF in request A does not affect request B.
  • SET / transaction state is not shared across calls.

Transactions

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.

If you need multi-statement atomicity, consider:

  • Using Turso's batch API (the backend uses it automatically for executemany())
  • Calling a server-side function that groups statements

Foreign key constraints

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.


Development & Testing

# Clone the repo
git clone https://github.com/CyberCalculus/django-libsql-backend.git
cd django-libsql-backend

# Install in development mode
pip install -e .

# Run Django checks
python manage.py check

# Run migrations
python manage.py migrate

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"

Check network connectivity to your Turso database URL. Increase the timeout in OPTIONS if needed:

"OPTIONS": {"timeout": 60}

"RuntimeError: No connection established"

The HTTP connection hasn't been initialized. This usually means the database settings are misconfigured — verify NAME and AUTH_TOKEN.

Migrations fail with "no such table: django_migrations"

Run python manage.py migrate — this table is created by Django's first migration.


License

MIT. See LICENSE for details.


Related Projects


Contributing

Issues and pull requests are welcome. Before opening a PR, please:

  1. Run python manage.py check against a Turso database
  2. Verify python manage.py migrate runs cleanly
  3. Test basic ORM operations (create, read, update, delete)

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

django_libsql_backend-0.1.0.tar.gz (19.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_libsql_backend-0.1.0-py3-none-any.whl (18.5 kB view details)

Uploaded Python 3

File details

Details for the file django_libsql_backend-0.1.0.tar.gz.

File metadata

  • Download URL: django_libsql_backend-0.1.0.tar.gz
  • Upload date:
  • Size: 19.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.12

File hashes

Hashes for django_libsql_backend-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e0c3a0e993f16424f63cffe723e8ae2d73d4ad7ad6e0e937eef51f2a51a82e62
MD5 8901f7cd72d00ee74311c291bfa7010b
BLAKE2b-256 71faf53e306ecc754622b53855dc185ed3fdb1207805c2f76d109d3891595c3a

See more details on using hashes here.

File details

Details for the file django_libsql_backend-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for django_libsql_backend-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8f6e4074d768b4bbea8ddfffd8af52523ee58529f36f92eafd885d3b54926ec3
MD5 c5b7211ffa0326d40ee91b6c878bd435
BLAKE2b-256 d1e02f55f74745366625c364435ce181de822c4b32bef2b26296ad251cbe17e1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page