Metadata-Version: 2.5
Name: django-turso
Version: 0.1.0
Summary: Native async Django database backend for Turso
Author-email: Paris Kasidiaris <paris@withlogic.co>
Keywords: async,database,django,sqlite,turso
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Database
Classifier: Typing :: Typed
Requires-Python: >=3.14
Requires-Dist: dj11>=0.1.0
Requires-Dist: django>=6.1
Requires-Dist: pyturso>=0.8.2rc2
Description-Content-Type: text/markdown

# django-turso

A Django database backend for [Turso](https://github.com/tursodatabase/turso) with native async execution.

Django still builds the queries and SQL. This backend replaces execution and row loading on the async path: a query suspends the calling asyncio task and the driver's completion descriptor wakes the same event loop. It starts no Python worker thread.

## Status

Alpha. The backend needs the native async binding (`turso.aio.native`) from the [withlogicco/turso](https://github.com/withlogicco/turso) fork of pyturso. pyturso on PyPI does not ship that module yet, so the backend fails to import with it. Install the fork after this package (see below).

## Install

```sh
pip install django-turso
pip install --force-reinstall --no-deps \
  "pyturso @ git+https://github.com/withlogicco/turso.git@22df675ff171ff567c8083dcf5308c3b12d106cb#subdirectory=bindings/python"
```

Building the fork needs a Rust toolchain; `maturin` builds the wheel.

Requirements:

- Linux.
- Python 3.14.
- Django 6.1.
- [dj11](https://pypi.org/project/dj11/), which owns the async ORM (`dj11.aorm`).

## Configure

```python
from django_turso.compat import connect_native

DATABASES = {
    "default": {
        "ENGINE": "django_turso.db.backends.turso",
        "NAME": BASE_DIR / "default.db",
        "OPTIONS": {
            "transaction_mode": "IMMEDIATE",
            "async_connect": connect_native,
            "strict_native_async": True,
        },
    },
}
```

`strict_native_async` rejects a driver that runs I/O on a Python worker thread. `connect_pyturso` opens pyturso's thread-based wrapper; use it only for compatibility tests.

Other `OPTIONS`:

- `pool.max_size`: number of extra read-only connections. They open on the same event loop and never write.
- `vfs` and `storage`: passed to the native driver.
- `init_command`: `;`-separated SQL run on each new connection.

## Use

Give a model the async manager from `dj11.aorm`:

```python
from django.db import models
from dj11.aorm.manager import AsyncManager


class Log(models.Model):
    service = models.TextField()
    observed_at = models.BigIntegerField()

    objects = AsyncManager()
```

Then query it:

```python
logs = Log.objects.filter(service="api")

async for log in logs.order_by("-observed_at"):
    print(log.service)

log = await logs.aget(pk=log_id)
count = await logs.acount()
await logs.abulk_create(batch)
```

Run migrations before serving traffic:

```sh
./manage.py migrate
```

The async path does not support related loading, joins, deferred loading, `select_for_update()`, conflict updates, or aggregates that need a subquery. These raise `dj11.aorm.exceptions.NotSupportedError` before any database I/O.

## What this package holds

- The backend, `django_turso.db.backends.turso`. It subclasses Django's SQLite backend and reuses its SQL.
- `?` placeholder conversion (`django_turso.sql`).
- A bounded compiled-statement cache keyed by SQL text (`django_turso.stmt_cache`).
- INSERT SQL-text reuse that skips Django's compiler for repeated insert shapes (`django_turso.inserts`).
- Driver factories and capability checks (`django_turso.compat`).
- Async connection cleanup (`django_turso.lifecycle`).
- `django_turso.driver.TursoConnection`, which adds `compact()` to the `dj11.aorm` connection protocol.

The database-independent async ORM (`AsyncQuerySet`, `AsyncManager`, the driver protocol) lives in `dj11.aorm`. This package re-exports none of it.

## Threads

Database I/O runs on the event loop's thread. To count threads, run a single Uvicorn worker without the autoreloader and read `/proc` in the process that owns the connections. Record native database threads apart from Python threads if a later engine revision adds any.
