mirror of
https://github.com/django/django.git
synced 2025-10-24 06:06:09 +00:00
Fixed #31224 -- Added support for asynchronous views and middleware.
This implements support for asynchronous views, asynchronous tests, asynchronous middleware, and an asynchronous test client.
This commit is contained in:
committed by
Mariusz Felisiak
parent
3f7e4b16bf
commit
fc0fa72ff4
@@ -71,6 +71,10 @@ method from the handler which takes care of applying :ref:`view middleware
|
||||
applying :ref:`template-response <template-response-middleware>` and
|
||||
:ref:`exception <exception-middleware>` middleware.
|
||||
|
||||
Middleware can either support only synchronous Python (the default), only
|
||||
asynchronous Python, or both. See :ref:`async-middleware` for details of how to
|
||||
advertise what you support, and know what kind of request you are getting.
|
||||
|
||||
Middleware can live anywhere on your Python path.
|
||||
|
||||
``__init__(get_response)``
|
||||
@@ -282,6 +286,81 @@ if the very next middleware in the chain raises an
|
||||
that exception; instead it will get an :class:`~django.http.HttpResponse`
|
||||
object with a :attr:`~django.http.HttpResponse.status_code` of 404.
|
||||
|
||||
.. _async-middleware:
|
||||
|
||||
Asynchronous support
|
||||
====================
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
Middleware can support any combination of synchronous and asynchronous
|
||||
requests. Django will adapt requests to fit the middleware's requirements if it
|
||||
cannot support both, but at a performance penalty.
|
||||
|
||||
By default, Django assumes that your middleware is capable of handling only
|
||||
synchronous requests. To change these assumptions, set the following attributes
|
||||
on your middleware factory function or class:
|
||||
|
||||
* ``sync_capable`` is a boolean indicating if the middleware can handle
|
||||
synchronous requests. Defaults to ``True``.
|
||||
|
||||
* ``async_capable`` is a boolean indicating if the middleware can handle
|
||||
asynchronous requests. Defaults to ``False``.
|
||||
|
||||
If your middleware has both ``sync_capable = True`` and
|
||||
``async_capable = True``, then Django will pass it the request in whatever form
|
||||
it is currently in. You can work out what type of request you have by seeing
|
||||
if the ``get_response`` object you are passed is a coroutine function or not
|
||||
(using :py:func:`asyncio.iscoroutinefunction`).
|
||||
|
||||
The ``django.utils.decorators`` module contains
|
||||
:func:`~django.utils.decorators.sync_only_middleware`,
|
||||
:func:`~django.utils.decorators.async_only_middleware`, and
|
||||
:func:`~django.utils.decorators.sync_and_async_middleware` decorators that
|
||||
allow you to apply these flags to middleware factory functions.
|
||||
|
||||
The returned callable must match the sync or async nature of the
|
||||
``get_response`` method. If you have an asynchronous ``get_response``, you must
|
||||
return a coroutine function (``async def``).
|
||||
|
||||
``process_view``, ``process_template_response`` and ``process_exception``
|
||||
methods, if they are provided, should also be adapted to match the sync/async
|
||||
mode. However, Django will individually adapt them as required if you do not,
|
||||
at an additional performance penalty.
|
||||
|
||||
Here's an example of how to detect and adapt your middleware if it supports
|
||||
both::
|
||||
|
||||
import asyncio
|
||||
from django.utils.decorators import sync_and_async_middleware
|
||||
|
||||
@sync_and_async_middleware
|
||||
def simple_middleware(get_response):
|
||||
# One-time configuration and initialization goes here.
|
||||
if asyncio.iscoroutinefunction(get_response):
|
||||
async def middleware(request):
|
||||
# Do something here!
|
||||
response = await get_response(request)
|
||||
return response
|
||||
|
||||
else:
|
||||
def middleware(request):
|
||||
# Do something here!
|
||||
response = get_response(request)
|
||||
return response
|
||||
|
||||
return middleware
|
||||
|
||||
.. note::
|
||||
|
||||
If you declare a hybrid middleware that supports both synchronous and
|
||||
asynchronous calls, the kind of call you get may not match the underlying
|
||||
view. Django will optimize the middleware call stack to have as few
|
||||
sync/async transitions as possible.
|
||||
|
||||
Thus, even if you are wrapping an async view, you may be called in sync
|
||||
mode if there is other, synchronous middleware between you and the view.
|
||||
|
||||
.. _upgrading-middleware:
|
||||
|
||||
Upgrading pre-Django 1.10-style middleware
|
||||
@@ -292,8 +371,8 @@ Upgrading pre-Django 1.10-style middleware
|
||||
|
||||
Django provides ``django.utils.deprecation.MiddlewareMixin`` to ease creating
|
||||
middleware classes that are compatible with both :setting:`MIDDLEWARE` and the
|
||||
old ``MIDDLEWARE_CLASSES``. All middleware classes included with Django
|
||||
are compatible with both settings.
|
||||
old ``MIDDLEWARE_CLASSES``, and support synchronous and asynchronous requests.
|
||||
All middleware classes included with Django are compatible with both settings.
|
||||
|
||||
The mixin provides an ``__init__()`` method that requires a ``get_response``
|
||||
argument and stores it in ``self.get_response``.
|
||||
@@ -345,3 +424,7 @@ These are the behavioral differences between using :setting:`MIDDLEWARE` and
|
||||
HTTP response, and then the next middleware in line will see that
|
||||
response. Middleware are never skipped due to a middleware raising an
|
||||
exception.
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
|
||||
Support for asynchronous requests was added to the ``MiddlewareMixin``.
|
||||
|
||||
@@ -202,3 +202,28 @@ in a test view. For example::
|
||||
response = self.client.get('/403/')
|
||||
# Make assertions on the response here. For example:
|
||||
self.assertContains(response, 'Error handler content', status_code=403)
|
||||
|
||||
.. _async-views:
|
||||
|
||||
Asynchronous views
|
||||
==================
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
As well as being synchronous functions, views can also be asynchronous
|
||||
functions (``async def``). Django will automatically detect these and run them
|
||||
in an asynchronous context. You will need to be using an asynchronous (ASGI)
|
||||
server to get the full power of them, however.
|
||||
|
||||
Here's an example of an asynchronous view::
|
||||
|
||||
from django.http import HttpResponse
|
||||
import datetime
|
||||
|
||||
async def current_datetime(request):
|
||||
now = datetime.datetime.now()
|
||||
html = '<html><body>It is now %s.</body></html>' % now
|
||||
return HttpResponse(html)
|
||||
|
||||
You can read more about Django's asynchronous support, and how to best use
|
||||
asynchronous views, in :doc:`/topics/async`.
|
||||
|
||||
Reference in New Issue
Block a user