django-async-backend


Namedjango-async-backend JSON
Version 0.0.2 PyPI version JSON
download
home_pagehttps://github.com/Arfey/django-async-backend
SummaryDjango extension providing async capabilities for database and other components
upload_time2025-10-22 22:53:43
maintainerNone
docs_urlNone
authorNone
requires_python>=3.12
licenseMIT
keywords django asyncio database
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 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"
}
        
Elapsed time: 2.23132s