Q-bart
Q-bart

Reputation: 1591

Async Django 3.1 with aiohttp client

Is it possible now, after the start of porting django to async to use aiohttp client with django?

My case:

  1. server receives a request;
  2. server sends another request to another server;
  3. server decodes the response, saves it into DB, and returns that response to the client;

Andrew Svetlov mentioned that aiohttp "was never intended to work with synchronous WSGI" two years ago. (https://github.com/aio-libs/aiohttp/issues/3357)

But how the situation looks now? Django seems to almost support async views. Can we use aiohttp with asgi django?


I know, I can create an aiohttp server that handles requests, then populates some queue, and queue handler that saves responses into database, but here I am missing a lot from django: ORM, admin, etc.

Upvotes: 1

Views: 3575

Answers (1)

Eduard Gan
Eduard Gan

Reputation: 615

You can implement thhis scenario in Django 3.1 like this:

async def my_proxy(request):
    async with ClientSession() as session:
        async with session.get('https://google.com') as resp:
            print(resp.status)
            print(await resp.text())

But the biggest question for me is how to share one session of aiohttp within django project because it is strongly not recommended to spawn a new ClientSession per request.

Important note: Of course you should run your django application in ASGI mode with some compatible application server(for example uvicorn):

uvicorn my_project.asgi:application --reload

UPDATE: I found a workaround. You can create a module(file *.py) with shared global objects and populate it with ClientSession instance from project settings at project startup:

shared.py:

from typing import Optional
from aiohttp import ClientSession


AIOHTTP_SESSION: Optional[ClientSession] = None

settings.py:

from aiohttp import ClientSession
from my_project import shared
...
shared.AIOHTTP_SESSION = ClientSession()

views.py:

from my_project import shared

async def my_proxy(request):
    async with shared.AIOHTTP_SESSION.get('https://google.com') as resp:
         print(resp.status, resp._session)
         await resp.text()

Important - imports should be EXACTLY the same. When i change them to form "from my_project.shared import AIOHTTP_SESSION" my test brakes :(

tests.py:

from asyncio import gather
from aiohttp import ClientSession
from django.test import TestCase
from my_project import shared


class ViewTests(TestCase):

    async def test_async_view(self):
        shared.AIOHTTP_SESSION = ClientSession()
        await gather(*[self.async_client.get('/async_view_url') for _ in range(3)])

Run test by ./manage.py test

Upvotes: 2

Related Questions