Browse Source

Fixed #28216 -- Added next_page/get_default_redirect_url() to LoginView.

ThinkChaos 7 years ago
parent
commit
b99d6c9cbc

+ 6 - 2
django/contrib/auth/views.py

@@ -43,6 +43,7 @@ class LoginView(SuccessURLAllowedHostsMixin, FormView):
     """
     form_class = AuthenticationForm
     authentication_form = None
+    next_page = None
     redirect_field_name = REDIRECT_FIELD_NAME
     template_name = 'registration/login.html'
     redirect_authenticated_user = False
@@ -63,8 +64,7 @@ class LoginView(SuccessURLAllowedHostsMixin, FormView):
         return super().dispatch(request, *args, **kwargs)
 
     def get_success_url(self):
-        url = self.get_redirect_url()
-        return url or resolve_url(settings.LOGIN_REDIRECT_URL)
+        return self.get_redirect_url() or self.get_default_redirect_url()
 
     def get_redirect_url(self):
         """Return the user-originating redirect URL if it's safe."""
@@ -79,6 +79,10 @@ class LoginView(SuccessURLAllowedHostsMixin, FormView):
         )
         return redirect_to if url_is_safe else ''
 
+    def get_default_redirect_url(self):
+        """Return the default redirect URL."""
+        return resolve_url(self.next_page or settings.LOGIN_REDIRECT_URL)
+
     def get_form_class(self):
         return self.authentication_form or self.form_class
 

+ 6 - 0
docs/releases/4.0.txt

@@ -47,6 +47,12 @@ Minor features
 * The default iteration count for the PBKDF2 password hasher is increased from
   260,000 to 320,000.
 
+* The new
+  :attr:`LoginView.next_page <django.contrib.auth.views.LoginView.next_page>`
+  attribute and
+  :meth:`~django.contrib.auth.views.LoginView.get_default_redirect_url` method
+  allow customizing the redirect after login.
+
 :mod:`django.contrib.contenttypes`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 19 - 2
docs/topics/auth/default.txt

@@ -996,17 +996,26 @@ implementation details see :ref:`using-the-views`.
     See :doc:`the URL documentation </topics/http/urls>` for details on using
     named URL patterns.
 
-    **Attributes:**
+    **Methods and Attributes**
 
     .. attribute:: template_name
 
         The name of a template to display for the view used to log the user in.
         Defaults to :file:`registration/login.html`.
 
+    .. attribute:: next_page
+
+        .. versionadded:: 4.0
+
+        The URL to redirect to after login. Defaults to
+        :setting:`LOGIN_REDIRECT_URL`.
+
     .. attribute:: redirect_field_name
 
         The name of a ``GET`` field containing the URL to redirect to after
-        login. Defaults to ``next``.
+        login. Defaults to ``next``. Overrides the
+        :meth:`get_default_redirect_url` URL if the given ``GET`` parameter is
+        passed.
 
     .. attribute:: authentication_form
 
@@ -1043,6 +1052,14 @@ implementation details see :ref:`using-the-views`.
         <django.http.HttpRequest.get_host>`, that are safe for redirecting
         after login. Defaults to an empty :class:`set`.
 
+    .. method:: get_default_redirect_url()
+
+        .. versionadded:: 4.0
+
+        Returns the URL to redirect to after login. The default implementation
+        resolves and returns :attr:`next_page` if set, or
+        :setting:`LOGIN_REDIRECT_URL` otherwise.
+
     Here's what ``LoginView`` does:
 
     * If called via ``GET``, it displays a login form that POSTs to the

+ 27 - 2
tests/auth_tests/test_views.py

@@ -52,8 +52,8 @@ class AuthViewsTestCase(TestCase):
         cls.u1 = User.objects.create_user(username='testclient', password='password', email='testclient@example.com')
         cls.u3 = User.objects.create_user(username='staff', password='password', email='staffmember@example.com')
 
-    def login(self, username='testclient', password='password'):
-        response = self.client.post('/login/', {
+    def login(self, username='testclient', password='password', url='/login/'):
+        response = self.client.post(url, {
             'username': username,
             'password': password,
         })
@@ -726,6 +726,31 @@ class LoginTest(AuthViewsTestCase):
         self.login()
         self.assertNotEqual(original_session_key, self.client.session.session_key)
 
+    def test_login_get_default_redirect_url(self):
+        response = self.login(url='/login/get_default_redirect_url/')
+        self.assertRedirects(response, '/custom/', fetch_redirect_response=False)
+
+    def test_login_next_page(self):
+        response = self.login(url='/login/next_page/')
+        self.assertRedirects(response, '/somewhere/', fetch_redirect_response=False)
+
+    def test_login_named_next_page_named(self):
+        response = self.login(url='/login/next_page/named/')
+        self.assertRedirects(response, '/password_reset/', fetch_redirect_response=False)
+
+    @override_settings(LOGIN_REDIRECT_URL='/custom/')
+    def test_login_next_page_overrides_login_redirect_url_setting(self):
+        response = self.login(url='/login/next_page/')
+        self.assertRedirects(response, '/somewhere/', fetch_redirect_response=False)
+
+    def test_login_redirect_url_overrides_next_page(self):
+        response = self.login(url='/login/next_page/?next=/test/')
+        self.assertRedirects(response, '/test/', fetch_redirect_response=False)
+
+    def test_login_redirect_url_overrides_get_default_redirect_url(self):
+        response = self.login(url='/login/get_default_redirect_url/?next=/test/')
+        self.assertRedirects(response, '/test/', fetch_redirect_response=False)
+
 
 class LoginURLSettings(AuthViewsTestCase):
     """Tests for settings.LOGIN_URL."""

+ 9 - 0
tests/auth_tests/urls.py

@@ -3,6 +3,7 @@ from django.contrib.auth import views
 from django.contrib.auth.decorators import login_required, permission_required
 from django.contrib.auth.forms import AuthenticationForm
 from django.contrib.auth.urls import urlpatterns as auth_urlpatterns
+from django.contrib.auth.views import LoginView
 from django.contrib.messages.api import info
 from django.http import HttpRequest, HttpResponse
 from django.shortcuts import render
@@ -78,6 +79,11 @@ def login_and_permission_required_exception(request):
     pass
 
 
+class CustomDefaultRedirectURLLoginView(LoginView):
+    def get_default_redirect_url(self):
+        return '/custom/'
+
+
 # special urls for auth test cases
 urlpatterns = auth_urlpatterns + [
     path('logout/custom_query/', views.LogoutView.as_view(redirect_field_name='follow')),
@@ -149,6 +155,9 @@ urlpatterns = auth_urlpatterns + [
          views.LoginView.as_view(redirect_authenticated_user=True)),
     path('login/allowed_hosts/',
          views.LoginView.as_view(success_url_allowed_hosts={'otherserver'})),
+    path('login/get_default_redirect_url/', CustomDefaultRedirectURLLoginView.as_view()),
+    path('login/next_page/', views.LoginView.as_view(next_page='/somewhere/')),
+    path('login/next_page/named/', views.LoginView.as_view(next_page='password_reset')),
 
     path('permission_required_redirect/', permission_required_redirect),
     path('permission_required_exception/', permission_required_exception),