# Django Async Backend
## ๐ Installation & Django Integration
### 1. Install the package
```bash
pip install django-async-backend
```
### 2. Django settings
In your settings.py, set the database engine to use the async backend
```python
DATABASES = {
"default": {
"ENGINE": "django_async_backend.db.backends.postgresql",
...
},
}
```
Make sure your Django app (and any other required apps) are listed in INSTALLED_APPS
```python
INSTALLED_APPS = [
...
"django_async_backend",
...
]
```
---
## Connection Handler
The connection handler manages database connections for your async backend.
```python
from django_async_backend.db import async_connections
connection = async_connections['default']
async with connection.cursor() as cursor:
await cursor.execute("SELECT ...")
rows = await cursor.fetchall()
```
- Connections are reused and managed automatically.
- Use await connection.close() to manually close a connection if needed.
Independent Connections & Parallel Queries
You can create a temporary, isolated connection for parallel or independent operations:
```python
import asyncio
from django_async_backend.db import async_connections
async def run_query():
async with async_connections.independent_connection():
conn = async_connections['default']
async with conn.cursor() as cursor:
await cursor.execute("SELECT ...")
return await cursor.fetchall()
results = await asyncio.gather(run_query(), run_query(), run_query())
```
## Cursor
Async cursors provide the following methods:
- `execute`
- `executemany`
- `fetchone`
- `fetchmany`
- `fetchall`
```python
async with connection.cursor() as cursor:
await cursor.execute("SELECT 1")
row = await cursor.fetchone()
```
## Async Transactions with `async_atomic`
### Basic Usage
Use `async_atomic` to run async database operations atomically.
All changes inside the block are committed together; if an error occurs, all changes are rolled back.
```python
from django_async_backend.db.transaction import async_atomic
async with async_atomic():
await create_instance(1)
# If no error, changes are committed
# If error, changes are rolled back
```
### Rollback on Error
If an exception is raised inside the block, all changes are rolled back:
```python
async with async_atomic():
await create_instance(1)
raise Exception("fail") # Nothing is committed
```
### Nested Transactions (Savepoints)
You can nest async_atomic blocks.
Each inner block creates a savepoint.
If an error occurs in the inner block, only its changes are rolled back; outer changes remain.
```python
async with async_atomic():
await create_instance(1)
try:
async with async_atomic():
await create_instance(2)
raise Exception("fail inner") # Only instance 2 is rolled back
except Exception:
pass
# Only instance 1 is in the database
```
### Using `on_commit` with async transactions
You can register a callback to run after a successful transaction commit using `connection.on_commit`.
```python
connection = async_connections[DEFAULT_DB_ALIAS]
async with async_atomic():
await connection.on_commit(callback)
```
## Writing Async Tests
### AsyncioTestCase
Use for async tests that do **not** require database transactions.
```python
from django_async_backend.test import AsyncioTestCase
class MyAsyncTests(AsyncioTestCase):
async def asyncSetUp(self):
# Setup code
async def asyncTearDown(self):
# Cleanup code
async def test_something(self):
# Your async test logic
await do_async_stuff()
```
### AsyncioTransactionTestCase
Use for async tests that need database transaction support (rollbacks, atomic blocks).
```python
from django_async_backend.test import AsyncioTransactionTestCase
class MyTransactionTests(AsyncioTransactionTestCase):
async def asyncSetUp(self):
# Setup database
async def asyncTearDown(self):
# Cleanup database
async def test_something(self):
async with async_atomic():
# DB operations here
await do_db_stuff()
```
<!--
# ORM support:
### Manager:
| methods | supported | comments |
| ----------------------------------- | --------- | -------- |
| `Model.objects.aget` | โ | |
| `Model.objects.acreate` | โ | |
| `Model.objects.acount` | โ | |
| `Model.objects.anone` | โ | |
| `Model.objects.abulk_create` | โ | |
| `Model.objects.abulk_update` | โ | |
| `Model.objects.aget_or_create` | โ | |
| `Model.objects.aupdate_or_create` | โ | |
| `Model.objects.aearliest` | โ | |
| `Model.objects.alatest` | โ | |
| `Model.objects.afirst` | โ | |
| `Model.objects.alast` | โ | |
| `Model.objects.ain_bulk` | โ | |
| `Model.objects.adelete` | โ | |
| `Model.objects.aupdate` | โ | |
| `Model.objects.aexists` | โ | |
| `Model.objects.aexplain` | โ | |
| `Model.objects.araw` | โ | |
| `Model.objects.aall` | โ | |
| `Model.objects.afilter` | โ | |
| `Model.objects.aexclude` | โ | |
| `Model.objects.acomplex_filter` | โ | |
| `Model.objects.aunion` | โ | |
| `Model.objects.aintersection` | โ | |
| `Model.objects.adifference` | โ | |
| `Model.objects.aselect_for_update` | โ | |
| `Model.objects.aprefetch_related` | โ | |
| `Model.objects.aannotate` | โ | |
| `Model.objects.aorder_by` | โ | |
| `Model.objects.adistinct` | โ | |
| `Model.objects.adifference` | โ | |
| `Model.objects.aextra` | โ | |
| `Model.objects.areverse` | โ | |
| `Model.objects.adefer` | โ | |
| `Model.objects.aonly` | โ | |
| `Model.objects.ausing` | โ | |
| `Model.objects.aresolve_expression` | โ | |
| `Model.objects.aordered` | โ | |
| `__aiter__` | โ | |
| `__repr__` | โ | |
| `__len__` | โ | |
| `__getitem__` | โ | |
| `Model.objects.aiterator` | โ | |
### RawQuerySet
Not supported โ
### Model:
| methods | supported | comments |
| --------------- | --------- | -------- |
| `Model.asave` | โ | |
| `Model.aupdate` | โ | |
| `Model.adelete` | โ | |
| `...` | โ | |
### User Model / Manager
| methods | supported | comments |
| --------------------------- | --------- | -------- |
| `User.is_authenticated` | โ | |
| `User.is_super_user` | โ | |
| `User.objects.acreate_user` | โ | |
| `...` | โ | | -->
## โ๏ธ Development Setup
**Install pre-commit hooks:**
```bash
pip install pre-commit
pre-commit install
```
**Install dependencies:**
```bash
poetry install --with dev
```
## ๐งช Running Tests
This project uses a comprehensive test suite powered by `unittest`.
**To run tests:**
```bash
docker-compose up postgres -d
DJANGO_SETTINGS_MODULE=settings poetry run python -m unittest discover -s tests
```
**Integration tests run locally.**
The `django_async_backend.db.backends.postgresql` backend is fully compatible with Django's default `django.db.backends.postgresql` backend, as it leverages the default implementation under the hood. To confirm this compatibility, run Django's test suite using the custom backend.
```python
DATABASES = {
"default": {
"ENGINE": "django_async_backend.db.backends.postgresql",
...
},
"other": {
"ENGINE": "django_async_backend.db.backends.postgresql",
...
},
}
```
To execute them:
```bash
cd tests_django
docker-compose run --build --rm test_django_integration
```
Raw data
{
"_id": null,
"home_page": "https://github.com/Arfey/django-async-backend",
"name": "django-async-backend",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "django, asyncio, database",
"author": null,
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/38/ab/659592aba5fbab5e98b74bcce936e34c1573ba72536b14be407217ba6bd3/django_async_backend-0.0.2.tar.gz",
"platform": null,
"description": "# Django Async Backend\n\n## \ud83d\ude80 Installation & Django Integration\n\n### 1. Install the package\n\n```bash\npip install django-async-backend\n```\n\n### 2. Django settings\n\nIn your settings.py, set the database engine to use the async backend\n\n```python\nDATABASES = {\n \"default\": {\n \"ENGINE\": \"django_async_backend.db.backends.postgresql\",\n ...\n },\n}\n```\n\nMake sure your Django app (and any other required apps) are listed in INSTALLED_APPS\n\n```python\nINSTALLED_APPS = [\n ...\n \"django_async_backend\",\n ...\n]\n```\n\n---\n\n## Connection Handler\n\nThe connection handler manages database connections for your async backend.\n\n```python\nfrom django_async_backend.db import async_connections\n\nconnection = async_connections['default']\n\nasync with connection.cursor() as cursor:\n await cursor.execute(\"SELECT ...\")\n rows = await cursor.fetchall()\n```\n\n- Connections are reused and managed automatically.\n- Use await connection.close() to manually close a connection if needed.\n\nIndependent Connections & Parallel Queries\nYou can create a temporary, isolated connection for parallel or independent operations:\n\n```python\nimport asyncio\nfrom django_async_backend.db import async_connections\n\nasync def run_query():\n async with async_connections.independent_connection():\n conn = async_connections['default']\n async with conn.cursor() as cursor:\n await cursor.execute(\"SELECT ...\")\n return await cursor.fetchall()\n\nresults = await asyncio.gather(run_query(), run_query(), run_query())\n```\n\n## Cursor\n\nAsync cursors provide the following methods:\n\n- `execute`\n- `executemany`\n- `fetchone`\n- `fetchmany`\n- `fetchall`\n\n```python\nasync with connection.cursor() as cursor:\n await cursor.execute(\"SELECT 1\")\n row = await cursor.fetchone()\n```\n\n## Async Transactions with `async_atomic`\n\n### Basic Usage\n\nUse `async_atomic` to run async database operations atomically.\nAll changes inside the block are committed together; if an error occurs, all changes are rolled back.\n\n```python\nfrom django_async_backend.db.transaction import async_atomic\n\nasync with async_atomic():\n await create_instance(1)\n # If no error, changes are committed\n # If error, changes are rolled back\n```\n\n### Rollback on Error\n\nIf an exception is raised inside the block, all changes are rolled back:\n\n```python\nasync with async_atomic():\n await create_instance(1)\n raise Exception(\"fail\") # Nothing is committed\n```\n\n### Nested Transactions (Savepoints)\n\nYou can nest async_atomic blocks.\nEach inner block creates a savepoint.\nIf an error occurs in the inner block, only its changes are rolled back; outer changes remain.\n\n```python\nasync with async_atomic():\n await create_instance(1)\n try:\n async with async_atomic():\n await create_instance(2)\n raise Exception(\"fail inner\") # Only instance 2 is rolled back\n except Exception:\n pass\n# Only instance 1 is in the database\n```\n\n### Using `on_commit` with async transactions\n\nYou can register a callback to run after a successful transaction commit using `connection.on_commit`.\n\n```python\nconnection = async_connections[DEFAULT_DB_ALIAS]\n\nasync with async_atomic():\n await connection.on_commit(callback)\n```\n\n## Writing Async Tests\n\n### AsyncioTestCase\n\nUse for async tests that do **not** require database transactions.\n\n```python\nfrom django_async_backend.test import AsyncioTestCase\n\n\nclass MyAsyncTests(AsyncioTestCase):\n async def asyncSetUp(self):\n # Setup code\n\n async def asyncTearDown(self):\n # Cleanup code\n\n async def test_something(self):\n # Your async test logic\n await do_async_stuff()\n```\n\n### AsyncioTransactionTestCase\n\nUse for async tests that need database transaction support (rollbacks, atomic blocks).\n\n```python\nfrom django_async_backend.test import AsyncioTransactionTestCase\n\n\nclass MyTransactionTests(AsyncioTransactionTestCase):\n async def asyncSetUp(self):\n # Setup database\n\n async def asyncTearDown(self):\n # Cleanup database\n\n async def test_something(self):\n async with async_atomic():\n # DB operations here\n await do_db_stuff()\n```\n\n<!--\n# ORM support:\n### Manager:\n\n| methods | supported | comments |\n| ----------------------------------- | --------- | -------- |\n| `Model.objects.aget` | \u274c | |\n| `Model.objects.acreate` | \u274c | |\n| `Model.objects.acount` | \u274c | |\n| `Model.objects.anone` | \u274c | |\n| `Model.objects.abulk_create` | \u274c | |\n| `Model.objects.abulk_update` | \u274c | |\n| `Model.objects.aget_or_create` | \u274c | |\n| `Model.objects.aupdate_or_create` | \u274c | |\n| `Model.objects.aearliest` | \u274c | |\n| `Model.objects.alatest` | \u274c | |\n| `Model.objects.afirst` | \u274c | |\n| `Model.objects.alast` | \u274c | |\n| `Model.objects.ain_bulk` | \u274c | |\n| `Model.objects.adelete` | \u274c | |\n| `Model.objects.aupdate` | \u274c | |\n| `Model.objects.aexists` | \u274c | |\n| `Model.objects.aexplain` | \u274c | |\n| `Model.objects.araw` | \u274c | |\n| `Model.objects.aall` | \u274c | |\n| `Model.objects.afilter` | \u274c | |\n| `Model.objects.aexclude` | \u274c | |\n| `Model.objects.acomplex_filter` | \u274c | |\n| `Model.objects.aunion` | \u274c | |\n| `Model.objects.aintersection` | \u274c | |\n| `Model.objects.adifference` | \u274c | |\n| `Model.objects.aselect_for_update` | \u274c | |\n| `Model.objects.aprefetch_related` | \u274c | |\n| `Model.objects.aannotate` | \u274c | |\n| `Model.objects.aorder_by` | \u274c | |\n| `Model.objects.adistinct` | \u274c | |\n| `Model.objects.adifference` | \u274c | |\n| `Model.objects.aextra` | \u274c | |\n| `Model.objects.areverse` | \u274c | |\n| `Model.objects.adefer` | \u274c | |\n| `Model.objects.aonly` | \u274c | |\n| `Model.objects.ausing` | \u274c | |\n| `Model.objects.aresolve_expression` | \u274c | |\n| `Model.objects.aordered` | \u274c | |\n| `__aiter__` | \u274c | |\n| `__repr__` | \u274c | |\n| `__len__` | \u274c | |\n| `__getitem__` | \u274c | |\n| `Model.objects.aiterator` | \u274c | |\n\n### RawQuerySet\n\nNot supported \u274c\n\n### Model:\n\n| methods | supported | comments |\n| --------------- | --------- | -------- |\n| `Model.asave` | \u274c | |\n| `Model.aupdate` | \u274c | |\n| `Model.adelete` | \u274c | |\n| `...` | \u274c | |\n\n### User Model / Manager\n\n| methods | supported | comments |\n| --------------------------- | --------- | -------- |\n| `User.is_authenticated` | \u274c | |\n| `User.is_super_user` | \u274c | |\n| `User.objects.acreate_user` | \u274c | |\n| `...` | \u274c | | -->\n\n\n## \u2699\ufe0f Development Setup\n\n**Install pre-commit hooks:**\n```bash\npip install pre-commit\npre-commit install\n```\n\n**Install dependencies:**\n```bash\npoetry install --with dev\n```\n\n## \ud83e\uddea Running Tests\n\nThis project uses a comprehensive test suite powered by `unittest`.\n\n**To run tests:**\n```bash\ndocker-compose up postgres -d\nDJANGO_SETTINGS_MODULE=settings poetry run python -m unittest discover -s tests\n```\n\n**Integration tests run locally.**\n\nThe `django_async_backend.db.backends.postgresql` backend is fully compatible with Django's default `django.db.backends.postgresql` backend, as it leverages the default implementation under the hood. To confirm this compatibility, run Django's test suite using the custom backend.\n\n```python\nDATABASES = {\n \"default\": {\n \"ENGINE\": \"django_async_backend.db.backends.postgresql\",\n ...\n },\n \"other\": {\n \"ENGINE\": \"django_async_backend.db.backends.postgresql\",\n ...\n },\n}\n```\n\nTo execute them:\n\n```bash\ncd tests_django\ndocker-compose run --build --rm test_django_integration\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Django extension providing async capabilities for database and other components",
"version": "0.0.2",
"project_urls": {
"Documentation": "https://github.com/Arfey/django-async-backend/blob/main/README.md",
"Homepage": "https://github.com/Arfey/django-async-backend",
"Repository": "https://github.com/Arfey/django-async-backend"
},
"split_keywords": [
"django",
" asyncio",
" database"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "0cd6e12ebecd060941c0c782112e059f2d2cbf1ed85d78c91edeeebcad570118",
"md5": "971fa063db66e1ba11782c596a2ae1d3",
"sha256": "b3a2400bcec18abbe8b7ab7c2d56e5da53c98608d4514a9dc5a783859e76846f"
},
"downloads": -1,
"filename": "django_async_backend-0.0.2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "971fa063db66e1ba11782c596a2ae1d3",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 28054,
"upload_time": "2025-10-22T22:53:42",
"upload_time_iso_8601": "2025-10-22T22:53:42.762176Z",
"url": "https://files.pythonhosted.org/packages/0c/d6/e12ebecd060941c0c782112e059f2d2cbf1ed85d78c91edeeebcad570118/django_async_backend-0.0.2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "38ab659592aba5fbab5e98b74bcce936e34c1573ba72536b14be407217ba6bd3",
"md5": "9a05c11b0c3bf0db2792e008dfdb8460",
"sha256": "4b69aa420ae579f9476a77667969000d9340557bafe6b5f51bba512d7dafbb50"
},
"downloads": -1,
"filename": "django_async_backend-0.0.2.tar.gz",
"has_sig": false,
"md5_digest": "9a05c11b0c3bf0db2792e008dfdb8460",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 24510,
"upload_time": "2025-10-22T22:53:43",
"upload_time_iso_8601": "2025-10-22T22:53:43.861585Z",
"url": "https://files.pythonhosted.org/packages/38/ab/659592aba5fbab5e98b74bcce936e34c1573ba72536b14be407217ba6bd3/django_async_backend-0.0.2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-22 22:53:43",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Arfey",
"github_project": "django-async-backend",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "django-async-backend"
}