Skip to main content

Async database operations for Django using psycopg3 async cursors and connection pooling (uses django-async-backend)

Project description

django-turbo-orm

Async database operations for Django using psycopg3 async cursors and connection pooling (uses django-async-backend).

Features

  • Async database I/O using psycopg3 async cursors
  • Connection pooling via psycopg_pool
  • Django's Query object for SQL generation
  • Familiar chainable queryset API

Requirements

  • Python 3.10+
  • Django 4.2+
  • PostgreSQL with psycopg3

Installation

pip install turbo-orm

Quick Start

1. Define your model

from django.db import models
from turbo_orm import AsyncManager

class User(models.Model):
    username = models.CharField(max_length=150)
    email = models.EmailField()
    is_active = models.BooleanField(default=True)

    # Add async manager
    objects = AsyncManager()

2. Use in async views

async def get_users(request):
    # Chainable (lazy, no DB hit)
    qs = User.objects.filter(is_active=True).order_by('-id')[:10]

    # Terminal (true async DB hit)
    users = await qs.alist()

    # Or iterate
    async for user in qs:
        print(user.username)

    # Single object
    user = await User.objects.aget(id=1)

    # Count
    count = await User.objects.filter(is_active=True).acount()

    # Create
    new_user = await User.objects.acreate(
        username='test',
        email='test@example.com'
    )

API

AsyncManager

Entry point attached to models, returns AsyncQuerySet.

User.objects.all()
User.objects.filter(is_active=True)
User.objects.exclude(username='admin')
await User.objects.aget(id=1)
await User.objects.acreate(username='new')
await User.objects.acount()

AsyncQuerySet

Chainable query builder with async terminal methods.

Chainable (no DB hit):

  • filter(), exclude()
  • order_by()
  • select_related(), prefetch_related()
  • only(), defer()
  • distinct()
  • values(), values_list()
  • Slicing: [:10]

Terminal (async DB hit):

  • await qs.aget() - Single object
  • await qs.afirst() - First or None
  • await qs.alast() - Last or None
  • await qs.acount() - Count
  • await qs.aexists() - Boolean exists
  • await qs.alist() - List of objects
  • await qs.acreate() - Create object
  • await qs.aupdate() - Bulk update
  • await qs.adelete() - Bulk delete
  • async for obj in qs - Async iteration

Why Turbo ORM?

Feature Django sync_to_async Turbo ORM
Thread pool Yes (overhead) No
Context switching Yes No
Memory per conn ~800KB ~200KB
Concurrent perf Baseline 2-4x faster

How It Works

Architecture

turbo-orm bridges Django's SQL generation with true async database execution:

┌─────────────────────────────────────────────────────────────────┐
│  Your Code                                                       │
│  await User.objects.filter(active=True).alist()                 │
└─────────────────────┬───────────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────────┐
│  AsyncQuerySet                                                   │
│  - Chainable methods build Django Query object                  │
│  - Terminal methods trigger execution                           │
└─────────────────────┬───────────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────────┐
│  Django SQLCompiler                                              │
│  - Generates SQL from Query object                              │
│  - Handles joins, filters, ordering                             │
└─────────────────────┬───────────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────────┐
│  turbo_orm.execution                                             │
│  - Gets connection from pool directly                           │
│  - Executes SQL with async cursor                               │
│  - Returns connection to pool                                   │
└─────────────────────┬───────────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────────┐
│  psycopg3 AsyncConnectionPool                                    │
│  - Manages pool of async PostgreSQL connections                 │
│  - Each request gets its own connection                         │
└─────────────────────────────────────────────────────────────────┘

Connection Pooling

turbo-orm uses django-async-backend with psycopg_pool for connection management.

The Problem with django-async-backend's Default Behavior:

django-async-backend uses thread-local storage for connections. In async code, all concurrent requests share the same thread, meaning they all fight over one connection wrapper:

# All 100 concurrent requests get the SAME wrapper
async_conn = async_connections["default"]  # Thread-local, shared!

Our Solution:

We bypass the thread-local wrapper and access the pool directly:

pool = async_connections["default"].pool

# Each request gets its OWN connection
conn = await pool.getconn()
try:
    cursor = conn.cursor()
    await cursor.execute(sql, params)
    rows = await cursor.fetchall()
finally:
    # Return THIS connection to pool (doesn't affect other requests)
    await pool.putconn(conn)

Flow with 100 concurrent requests:

Request 1 ──→ pool.getconn() ──→ Connection A ──→ query ──→ pool.putconn(A)
Request 2 ──→ pool.getconn() ──→ Connection B ──→ query ──→ pool.putconn(B)
Request 3 ──→ pool.getconn() ──→ Connection C ──→ query ──→ pool.putconn(C)
...

Each request has isolated connection lifecycle. No conflicts, no pool exhaustion.

Configuration

Configure pooling in Django settings:

DATABASES = {
    "default": {
        "ENGINE": "django_async_backend.db.backends.postgresql",
        "NAME": "mydb",
        "USER": "postgres",
        "PASSWORD": "postgres",
        "HOST": "localhost",
        "PORT": "5432",
        "CONN_MAX_AGE": 0,
        "OPTIONS": {
            "pool": {
                "min_size": 5,   # Minimum connections in pool
                "max_size": 20,  # Maximum connections in pool
            }
        },
    }
}

Dependencies

  • django-async-backend - Async database backend for Django
  • psycopg[binary,pool] - PostgreSQL adapter with async support and pooling

License

MIT

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_turbo_orm-0.1.0.tar.gz (34.0 kB view details)

Uploaded Source

Built Distribution

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

django_turbo_orm-0.1.0-py3-none-any.whl (15.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: django_turbo_orm-0.1.0.tar.gz
  • Upload date:
  • Size: 34.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for django_turbo_orm-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8d3902911bb019f995294a4d2a793b6cfe3a0012b194aef336e07d586aba818c
MD5 360d1534e14c4754bf6d9dc3f7f0070b
BLAKE2b-256 a4bcec3aaa7f7aa15d3357c7dc967329d6546deddb0ceff4ee68e44c0edf3fc8

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_turbo_orm-0.1.0.tar.gz:

Publisher: publish.yml on FarhanAliRaza/turbo-orm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

File hashes

Hashes for django_turbo_orm-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4299838489ff9800703f472e2562c6268e40628e61774b5621c1a8b668ad4a2d
MD5 446bff52dcd1f4c1dabda4522491af8c
BLAKE2b-256 bacc2eda247423cc9981a6a96855351a47d80a5f5f176b01566ed1486ac51221

See more details on using hashes here.

Provenance

The following attestation bundles were made for django_turbo_orm-0.1.0-py3-none-any.whl:

Publisher: publish.yml on FarhanAliRaza/turbo-orm

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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