Django 3.1 async view is done (=> async phase 2 is done !)

History

Phase 1 : Django 3.0

Phase 2 : Django 3.1

../../_images/phase_2.png

https://speakerdeck.com/andrewgodwin/just-add-await-retrofitting-async-into-django?slide=61 https://speakerdeck.com/andrewgodwin/just-add-await-retrofitting-async-into-django?slide=1

Django 3.1 announce

To get started with async views , you need to declare a view using async def :

Example 1

# https://docs.python.org/3/library/asyncio.html
# https://docs.python.org/3/library/asyncio-api-index.html
import asyncio

async def my_view(request):
    await asyncio.sleep(0.5)
    return HttpResponse('Hello, async world!')

Example 2

models.py

 1 # https://docs.python.org/3/library/asyncio-task.html
 2 import asyncio
 3 # https://docs.djangoproject.com/en/3.1/topics/async/#async-to-sync
 4 from asgiref.sync import sync_to_async
 5 def sync_update_conges():
 6     print("Begin sync_update_conges()")
 7     PROJETS_CONGES = Projet.objects.filter(designation__icontains="abs")
 8     PROJETS_CONGES_ID = [p.id for p in PROJETS_CONGES]
 9
10
11 loop = asyncio.get_event_loop()
12 async_function = sync_to_async(sync_update_conges)
13 loop.create_task(async_function())

Example 3 async Django

urls.py

 1 path("asyncview/test1", views_asgi.async_view_test_1, name="asgi_async_view_test1"),
 2 path("syncview/test1", views_asgi.sync_view_test_1, name="asgi_sync_view_test1"),
 3 path(
 4     "asyncview/smoke_some_meats",
 5     views_asgi.smoke_some_meats,
 6     name="asgi_async_smoke_some_meats",
 7 ),
 8 path(
 9     "asyncview/async_hello", views_asgi.async_hello, name="asgi_async_async_hello"
10 ),

views_asgi.py

 1 # https://docs.python.org/3/library/asyncio-task.html
 2 import asyncio
 3 # https://docs.djangoproject.com/en/3.1/topics/async/#async-to-sync
 4 from asgiref.sync import sync_to_async
 5
 6 def orm_get_fiches_temps_en_calcul() -> None:
 7     """Incrément du temps passé pour les FICHE_EN_CALCUL."""
 8     now = datetime.now()
 9     logger.info(
10         f"Begin task_update_fiches_temps_en_calcul() {now.hour:02}H{now.minute:02}mn{now.second:02}s"
11     )
12     liste_fiches_en_calcul = None
13     try:
14         liste_fiches_en_calcul = FicheTemps.objects.filter(
15             etat=FICHE_STATUS["FICHE_EN_CALCUL"]
16         )
17         if liste_fiches_en_calcul.count() == 0:
18             now = datetime.now()
19             logger.info(
20                 f"pas de fiches en calcul {now.hour:02}H{now.minute:02}mn{now.second:02}s"
21             )
22         else:
23             # parcours des fiches de temps en calcul
24             for fiches_temps in liste_fiches_en_calcul:
25                 try:
26                     fiches_temps.temps_impute = fiches_temps.temps_impute + timedelta(
27                         seconds=INCREMENT_TIME_SECONDS
28                     )
29                     logger.info(f"Temps impute:{fiches_temps.temps_impute}")
30                     fiches_temps.save()
31                 except:
32                     logger.error("task_update_fiches_temps_en_calcul()")
33
34     except Exception as error:
35         logger.error(f"Pb in task_update_fiches_temps_en_calcul = {error=}")
36
37     return liste_fiches_en_calcul
38
39
40 async def _async_hello():
41     while True:
42         loop = asyncio.get_event_loop()
43         async_function = sync_to_async(orm_get_fiches_temps_en_calcul)
44         loop.create_task(async_function())
45         await asyncio.sleep(5)
46
47
48 async def async_hello(request: HttpRequest) -> HttpResponse:
49     """Tâche périodique asynchrone.
50
51     http://localhost:8004/fiches_temps/asyncview/async_hello
52     """
53     loop = asyncio.get_event_loop()
54     loop.create_task(_async_hello())
55     return HttpResponse(f"Non blocking hello")

All asynchronous features are supported whether you are running under WSGI or ASGI mode .

However, there will be performance penalties using async code in WSGI mode .

You can read more about the specifics in Asynchronous support documentation .

You are free to mix async and sync views, middleware, and tests as much as you want. Django will ensure that you always end up with the right execution context.

We expect most projects will keep the majority of their views synchronous, and only have a select few running in async mode but it is entirely your choice .

Django’s ORM, cache layer, and other pieces of code that do long-running network calls do not yet support async access .

We expect to add support for them in upcoming releases .

Async views are ideal, however, if you are doing a lot of API or HTTP calls inside your view, you can now natively do all those HTTP calls in parallel to considerably speed up your view’s execution.

Asynchronous support should be entirely backwards-compatible and we have tried to ensure that it has no speed regressions for your existing, synchronous code.

It should have no noticeable effect on any existing Django projects.

Mariusz Felisiak

Many thanks to @andrewgodwin for adding support for async views, middleware, and tests to #Django #async

../../_images/async_views.png

https://fediverse.org/MariuszFelisiak/status/1240357596315467778?s=20

Andrew Godwin

It is done - Django 3.1 will have async views ! Thanks to everyone who’s helped out with this.

../../_images/async_views_godwin.png

https://fediverse.org/andrewgodwin/status/1240357729174052866?s=20

The fc0fa72ff4cdb git commit

Simon Willison

../../_images/simon_willison.png

https://fediverse.org/simonw/status/1287146436837044224?s=20

Node is a tempting option for anything involving comet, file uploads or
even just mashing together potentially slow loading web APIs.

10.5 years later, I'm excited about async views in Django 3.1 for exactly
the same reasons (except we don't call live updates "comet" any more)

Tutorials