Browse Source

Fixed #31920 -- Made AuthenticationMiddleware add request.auser().

Jon Janzen 2 years ago
parent
commit
e846c5e724

+ 11 - 0
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):

+ 14 - 0
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

+ 3 - 0
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`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 15 - 0
docs/topics/auth/default.txt

@@ -368,6 +368,7 @@ Django uses :doc:`sessions </topics/http/sessions>` and middleware to hook the
 authentication system into :class:`request objects <django.http.HttpRequest>`.
 
 These provide a :attr:`request.user <django.http.HttpRequest.user>`  attribute
+and a :meth:`request.auser <django.http.HttpRequest.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

+ 7 - 0
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)