Refs #31949 -- Made @vary_on_(cookie/headers) decorators work with async functions.

This commit is contained in:
Ben Lomax 2023-07-08 21:54:37 +01:00 committed by Mariusz Felisiak
parent fb1c763506
commit b7a17b0ea0
5 changed files with 97 additions and 6 deletions

View File

@ -1,5 +1,7 @@
from functools import wraps
from asgiref.sync import iscoroutinefunction
from django.utils.cache import patch_vary_headers
@ -16,13 +18,21 @@ def vary_on_headers(*headers):
"""
def decorator(func):
@wraps(func)
def inner_func(*args, **kwargs):
response = func(*args, **kwargs)
patch_vary_headers(response, headers)
return response
if iscoroutinefunction(func):
return inner_func
async def _view_wrapper(request, *args, **kwargs):
response = await func(request, *args, **kwargs)
patch_vary_headers(response, headers)
return response
else:
def _view_wrapper(request, *args, **kwargs):
response = func(request, *args, **kwargs)
patch_vary_headers(response, headers)
return response
return wraps(func)(_view_wrapper)
return decorator

View File

@ -268,6 +268,8 @@ Decorators
* :func:`~django.views.decorators.http.require_GET`
* :func:`~django.views.decorators.http.require_POST`
* :func:`~django.views.decorators.http.require_safe`
* :func:`~django.views.decorators.vary.vary_on_cookie`
* :func:`~django.views.decorators.vary.vary_on_headers`
* ``xframe_options_deny()``
* ``xframe_options_sameorigin()``
* ``xframe_options_exempt()``

View File

@ -94,6 +94,8 @@ view functions:
* :func:`~django.views.decorators.http.require_GET`
* :func:`~django.views.decorators.http.require_POST`
* :func:`~django.views.decorators.http.require_safe`
* :func:`~django.views.decorators.vary.vary_on_cookie`
* :func:`~django.views.decorators.vary.vary_on_headers`
* ``xframe_options_deny()``
* ``xframe_options_sameorigin()``
* ``xframe_options_exempt()``

View File

@ -115,6 +115,10 @@ caching based on specific request headers.
.. function:: vary_on_cookie(func)
.. versionchanged:: 5.0
Support for wrapping asynchronous view functions was added.
.. function:: vary_on_headers(*headers)
The ``Vary`` header defines which request headers a cache mechanism should take
@ -122,6 +126,10 @@ caching based on specific request headers.
See :ref:`using vary headers <using-vary-headers>`.
.. versionchanged:: 5.0
Support for wrapping asynchronous view functions was added.
.. module:: django.views.decorators.cache
Caching

View File

@ -0,0 +1,69 @@
from asgiref.sync import iscoroutinefunction
from django.http import HttpRequest, HttpResponse
from django.test import SimpleTestCase
from django.views.decorators.vary import vary_on_cookie, vary_on_headers
class VaryOnHeadersTests(SimpleTestCase):
def test_wrapped_sync_function_is_not_coroutine_function(self):
def sync_view(request):
return HttpResponse()
wrapped_view = vary_on_headers()(sync_view)
self.assertIs(iscoroutinefunction(wrapped_view), False)
def test_wrapped_async_function_is_coroutine_function(self):
async def async_view(request):
return HttpResponse()
wrapped_view = vary_on_headers()(async_view)
self.assertIs(iscoroutinefunction(wrapped_view), True)
def test_vary_on_headers_decorator(self):
@vary_on_headers("Header", "Another-header")
def sync_view(request):
return HttpResponse()
response = sync_view(HttpRequest())
self.assertEqual(response.get("Vary"), "Header, Another-header")
async def test_vary_on_headers_decorator_async_view(self):
@vary_on_headers("Header", "Another-header")
async def async_view(request):
return HttpResponse()
response = await async_view(HttpRequest())
self.assertEqual(response.get("Vary"), "Header, Another-header")
class VaryOnCookieTests(SimpleTestCase):
def test_wrapped_sync_function_is_not_coroutine_function(self):
def sync_view(request):
return HttpResponse()
wrapped_view = vary_on_cookie(sync_view)
self.assertIs(iscoroutinefunction(wrapped_view), False)
def test_wrapped_async_function_is_coroutine_function(self):
async def async_view(request):
return HttpResponse()
wrapped_view = vary_on_cookie(async_view)
self.assertIs(iscoroutinefunction(wrapped_view), True)
def test_vary_on_cookie_decorator(self):
@vary_on_cookie
def sync_view(request):
return HttpResponse()
response = sync_view(HttpRequest())
self.assertEqual(response.get("Vary"), "Cookie")
async def test_vary_on_cookie_decorator_async_view(self):
@vary_on_cookie
async def async_view(request):
return HttpResponse()
response = await async_view(HttpRequest())
self.assertEqual(response.get("Vary"), "Cookie")