1

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.

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

1 Comment

That's good you point out that aiohttp.ClientSession should be created once for the lifetime of the server. This is in order to benefit from connection pooling. One thing though is that per docs the ClientSession should be created inside an async function: "To avoid import dependency hell aiohttp encourages creation of ClientSession from an async function."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.