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 objectawait qs.afirst()- First or Noneawait qs.alast()- Last or Noneawait qs.acount()- Countawait qs.aexists()- Boolean existsawait qs.alist()- List of objectsawait qs.acreate()- Create objectawait qs.aupdate()- Bulk updateawait qs.adelete()- Bulk deleteasync 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 Djangopsycopg[binary,pool]- PostgreSQL adapter with async support and pooling
License
MIT
Project details
Release history Release notifications | RSS feed
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_turbo_orm-0.1.1.tar.gz.
File metadata
- Download URL: django_turbo_orm-0.1.1.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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d40af2086afbfdb1c454ce67c7a94d812e5a8b157e92abd6006aa754848c43c1
|
|
| MD5 |
cbf84b67709edc04f23cce4cc5e99f2a
|
|
| BLAKE2b-256 |
e5fcdf9e4409ab85f589c195c4c272f6dbbd12c2915744bb8dbe574d9f8c02ae
|
Provenance
The following attestation bundles were made for django_turbo_orm-0.1.1.tar.gz:
Publisher:
publish.yml on FarhanAliRaza/turbo-orm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_turbo_orm-0.1.1.tar.gz -
Subject digest:
d40af2086afbfdb1c454ce67c7a94d812e5a8b157e92abd6006aa754848c43c1 - Sigstore transparency entry: 814875983
- Sigstore integration time:
-
Permalink:
FarhanAliRaza/turbo-orm@b7da7ef676338266841089d3388f5f2f09b3c1c0 -
Branch / Tag:
refs/tags/0.1.1 - Owner: https://github.com/FarhanAliRaza
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b7da7ef676338266841089d3388f5f2f09b3c1c0 -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_turbo_orm-0.1.1-py3-none-any.whl.
File metadata
- Download URL: django_turbo_orm-0.1.1-py3-none-any.whl
- Upload date:
- Size: 15.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
387a5cb5b3f2ffb52b47178b8a74fd80920fc844cf4b8699bc80fa047e57080d
|
|
| MD5 |
38770539fc9c3aab62099690aedd215d
|
|
| BLAKE2b-256 |
f299cb7e1572ad61f58bedf944024c57765a0016c2070d30fe370d64e472ead2
|
Provenance
The following attestation bundles were made for django_turbo_orm-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on FarhanAliRaza/turbo-orm
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_turbo_orm-0.1.1-py3-none-any.whl -
Subject digest:
387a5cb5b3f2ffb52b47178b8a74fd80920fc844cf4b8699bc80fa047e57080d - Sigstore transparency entry: 814875987
- Sigstore integration time:
-
Permalink:
FarhanAliRaza/turbo-orm@b7da7ef676338266841089d3388f5f2f09b3c1c0 -
Branch / Tag:
refs/tags/0.1.1 - Owner: https://github.com/FarhanAliRaza
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b7da7ef676338266841089d3388f5f2f09b3c1c0 -
Trigger Event:
release
-
Statement type: