diff --git a/django/contrib/auth/middleware.py b/django/contrib/auth/middleware.py index dcc482154c6..72dc553827e 100644 --- a/django/contrib/auth/middleware.py +++ b/django/contrib/auth/middleware.py @@ -1,3 +1,7 @@ +from functools import partial + +from asgiref.sync import sync_to_async + from django.contrib import auth from django.contrib.auth import load_backend from django.contrib.auth.backends import RemoteUserBackend @@ -12,6 +16,12 @@ def get_user(request): return request._cached_user +async def auser(request): + if not hasattr(request, "_acached_user"): + request._acached_user = await sync_to_async(auth.get_user)(request) + return request._acached_user + + class AuthenticationMiddleware(MiddlewareMixin): def process_request(self, request): if not hasattr(request, "session"): @@ -23,6 +33,7 @@ class AuthenticationMiddleware(MiddlewareMixin): "'django.contrib.auth.middleware.AuthenticationMiddleware'." ) request.user = SimpleLazyObject(lambda: get_user(request)) + request.auser = partial(auser, request) class RemoteUserMiddleware(MiddlewareMixin): diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 9b0ed76e4d4..9b71adfdf98 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -281,9 +281,23 @@ middleware class is listed in :setting:`MIDDLEWARE`. else: ... # Do something for anonymous users. + The :meth:`auser` method does the same thing but can be used from async + contexts. + Methods ------- +.. method:: HttpRequest.auser() + + .. versionadded:: 5.0 + + From the :class:`~django.contrib.auth.middleware.AuthenticationMiddleware`: + Coroutine. Returns an instance of :setting:`AUTH_USER_MODEL` representing + the currently logged-in user. If the user isn't currently logged in, + ``auser`` will return an instance of + :class:`~django.contrib.auth.models.AnonymousUser`. This is similar to the + :attr:`user` attribute but it works in async contexts. + .. method:: HttpRequest.get_host() Returns the originating host of the request using information from the diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt index 83cb6284a51..21ab783f86c 100644 --- a/docs/releases/5.0.txt +++ b/docs/releases/5.0.txt @@ -65,6 +65,9 @@ Minor features * The default iteration count for the PBKDF2 password hasher is increased from 600,000 to 720,000. +* ``AuthenticationMiddleware`` now adds an :meth:`.HttpRequest.auser` + asynchronous method that returns the currently logged-in user. + :mod:`django.contrib.contenttypes` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt index a93201fb09a..c705abcb6b0 100644 --- a/docs/topics/auth/default.txt +++ b/docs/topics/auth/default.txt @@ -368,6 +368,7 @@ Django uses :doc:`sessions ` and middleware to hook the authentication system into :class:`request objects `. These provide a :attr:`request.user ` attribute +and a :meth:`request.auser ` async method on every request which represents the current user. If the current user has not logged in, this attribute will be set to an instance of :class:`~django.contrib.auth.models.AnonymousUser`, otherwise it will be an @@ -383,6 +384,20 @@ You can tell them apart with # Do something for anonymous users. ... +Or in an asynchronous view:: + + user = await request.auser() + if user.is_authenticated: + # Do something for authenticated users. + ... + else: + # Do something for anonymous users. + ... + +.. versionchanged:: 5.0 + + The :meth:`.HttpRequest.auser` method was added. + .. _how-to-log-a-user-in: How to log a user in diff --git a/tests/auth_tests/test_middleware.py b/tests/auth_tests/test_middleware.py index 3d2d0fcb48a..e7c5a525cd3 100644 --- a/tests/auth_tests/test_middleware.py +++ b/tests/auth_tests/test_middleware.py @@ -43,3 +43,10 @@ class TestAuthenticationMiddleware(TestCase): ) with self.assertRaisesMessage(ImproperlyConfigured, msg): self.middleware(HttpRequest()) + + async def test_auser(self): + self.middleware(self.request) + auser = await self.request.auser() + self.assertEqual(auser, self.user) + auser_second = await self.request.auser() + self.assertIs(auser, auser_second)