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 import auth
 from django.contrib.auth import load_backend
 from django.contrib.auth import load_backend
 from django.contrib.auth.backends import RemoteUserBackend
 from django.contrib.auth.backends import RemoteUserBackend
@@ -12,6 +16,12 @@ def get_user(request):
     return request._cached_user
     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):
 class AuthenticationMiddleware(MiddlewareMixin):
     def process_request(self, request):
     def process_request(self, request):
         if not hasattr(request, "session"):
         if not hasattr(request, "session"):
@@ -23,6 +33,7 @@ class AuthenticationMiddleware(MiddlewareMixin):
                 "'django.contrib.auth.middleware.AuthenticationMiddleware'."
                 "'django.contrib.auth.middleware.AuthenticationMiddleware'."
             )
             )
         request.user = SimpleLazyObject(lambda: get_user(request))
         request.user = SimpleLazyObject(lambda: get_user(request))
+        request.auser = partial(auser, request)
 
 
 
 
 class RemoteUserMiddleware(MiddlewareMixin):
 class RemoteUserMiddleware(MiddlewareMixin):

+ 14 - 0
docs/ref/request-response.txt

@@ -281,9 +281,23 @@ middleware class is listed in :setting:`MIDDLEWARE`.
         else:
         else:
             ...  # Do something for anonymous users.
             ...  # Do something for anonymous users.
 
 
+    The :meth:`auser` method does the same thing but can be used from async
+    contexts.
+
 Methods
 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()
 .. method:: HttpRequest.get_host()
 
 
     Returns the originating host of the request using information from the
     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
 * The default iteration count for the PBKDF2 password hasher is increased from
   600,000 to 720,000.
   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`
 :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>`.
 authentication system into :class:`request objects <django.http.HttpRequest>`.
 
 
 These provide a :attr:`request.user <django.http.HttpRequest.user>`  attribute
 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
 on every request which represents the current user. If the current user has not
 logged in, this attribute will be set to an instance
 logged in, this attribute will be set to an instance
 of :class:`~django.contrib.auth.models.AnonymousUser`, otherwise it will be an
 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.
         # 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:
 
 
 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):
         with self.assertRaisesMessage(ImproperlyConfigured, msg):
             self.middleware(HttpRequest())
             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)