Django In Depth 1. 2. 3. Threading, cooperation, and intrigue Spanning two worlds with one vision Handlers, Requests, Middleware & Views Big Framework Problems Async In Brief
Async has to add, not replace Sync Django is still important Things need to look familiar We don't want a wildly different API feel Things need to be safe Deadlocking or blocking is easier than ever
Phase One: ASGI Support Allowing Django to be async at all Phase Two: Async Views Unlocking async use in normal apps Phase Three: The ORM High-level async use for the most common case
Phase One: Django 3.0 It's already committed! Phase Two: Django 3.1 Unless things really do turn out nicely... Phase Three: Django 3.2/4.0 There's a lot of work here.
“ James Bennett, "Django and NIH", 2006 Just so you know, Django is a smug, arrogant framework that doesn’t play nice with others. [...] Or at least, that’s the impression you’d get from reading the rants...
Custom request/response objects Most other frameworks did this too Custom "handler" classes Abstracts away WSGI Custom middleware Wow, was this contentious at the time!
body_file = tempfile.SpooledTemporaryFile(max_size=..., mode='w+b') while True: message = await receive() if message['type'] == 'http.disconnect': # Early client disconnect. raise RequestAborted() # Add a body chunk from the message, if provided. if 'body' in message: body_file.write(message['body']) # Quit out if that's the end. if not message.get('more_body', False): break body_file.seek(0) return body_file
if response.streaming: # Access `__iter__` and not `streaming_content` directly in case # it has been overridden in a subclass. for part in response: for chunk, _ in self.chunk_bytes(part): await send({ 'type': 'http.response.body', 'body': chunk, # Ignore "more" as there may be more parts; instead, # use an empty final closing message with False. 'more_body': True, }) # Final closing message. await send({'type': 'http.response.body'})
Propagates exceptions nicely Really helps with debugging! Proxies threadlocals down correctly Because people really love threadlocals. Stickies sync code into one thread We'll get back to this. It's nasty.
Transactions Views are auto-wrapped in them with ATOMIC_REQUESTS Templates Direct calls from error handlers Tracebacks They're really long with all the switch functions
# Iteration is the one transparent thing for result in Model.objects.filter(name="Andrew"): >>> QuerySet.__iter__ # This can work in the same codebase! async for result in Model.objects.filter(name="Andrew"): >>> QuerySet.__aiter__
async def random_code(): result = Model.objects.get(pk=5) >>> SynchronousOnlyOperation("You cannot call this from an async context - use a thread or sync_to_async.")
aeracode.org/2018/02/19/python-async-simplified/ A deeper dive into async vs. sync functions github.com/django/deps/blob/master/accepted/0009-async.rst DEP 0009, the proposal for async in Django code.djangoproject.com/wiki/AsyncProject Where to go to help