Skip to content

Commit b99d6c9

Browse files
ThinkChaosfelixxm
authored andcommitted
Fixed #28216 -- Added next_page/get_default_redirect_url() to LoginView.
1 parent 5984117 commit b99d6c9

File tree

5 files changed

+67
-6
lines changed

5 files changed

+67
-6
lines changed

django/contrib/auth/views.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class LoginView(SuccessURLAllowedHostsMixin, FormView):
4343
"""
4444
form_class = AuthenticationForm
4545
authentication_form = None
46+
next_page = None
4647
redirect_field_name = REDIRECT_FIELD_NAME
4748
template_name = 'registration/login.html'
4849
redirect_authenticated_user = False
@@ -63,8 +64,7 @@ def dispatch(self, request, *args, **kwargs):
6364
return super().dispatch(request, *args, **kwargs)
6465

6566
def get_success_url(self):
66-
url = self.get_redirect_url()
67-
return url or resolve_url(settings.LOGIN_REDIRECT_URL)
67+
return self.get_redirect_url() or self.get_default_redirect_url()
6868

6969
def get_redirect_url(self):
7070
"""Return the user-originating redirect URL if it's safe."""
@@ -79,6 +79,10 @@ def get_redirect_url(self):
7979
)
8080
return redirect_to if url_is_safe else ''
8181

82+
def get_default_redirect_url(self):
83+
"""Return the default redirect URL."""
84+
return resolve_url(self.next_page or settings.LOGIN_REDIRECT_URL)
85+
8286
def get_form_class(self):
8387
return self.authentication_form or self.form_class
8488

docs/releases/4.0.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ Minor features
4747
* The default iteration count for the PBKDF2 password hasher is increased from
4848
260,000 to 320,000.
4949

50+
* The new
51+
:attr:`LoginView.next_page <django.contrib.auth.views.LoginView.next_page>`
52+
attribute and
53+
:meth:`~django.contrib.auth.views.LoginView.get_default_redirect_url` method
54+
allow customizing the redirect after login.
55+
5056
:mod:`django.contrib.contenttypes`
5157
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5258

docs/topics/auth/default.txt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -996,17 +996,26 @@ implementation details see :ref:`using-the-views`.
996996
See :doc:`the URL documentation </topics/http/urls>` for details on using
997997
named URL patterns.
998998

999-
**Attributes:**
999+
**Methods and Attributes**
10001000

10011001
.. attribute:: template_name
10021002

10031003
The name of a template to display for the view used to log the user in.
10041004
Defaults to :file:`registration/login.html`.
10051005

1006+
.. attribute:: next_page
1007+
1008+
.. versionadded:: 4.0
1009+
1010+
The URL to redirect to after login. Defaults to
1011+
:setting:`LOGIN_REDIRECT_URL`.
1012+
10061013
.. attribute:: redirect_field_name
10071014

10081015
The name of a ``GET`` field containing the URL to redirect to after
1009-
login. Defaults to ``next``.
1016+
login. Defaults to ``next``. Overrides the
1017+
:meth:`get_default_redirect_url` URL if the given ``GET`` parameter is
1018+
passed.
10101019

10111020
.. attribute:: authentication_form
10121021

@@ -1043,6 +1052,14 @@ implementation details see :ref:`using-the-views`.
10431052
<django.http.HttpRequest.get_host>`, that are safe for redirecting
10441053
after login. Defaults to an empty :class:`set`.
10451054

1055+
.. method:: get_default_redirect_url()
1056+
1057+
.. versionadded:: 4.0
1058+
1059+
Returns the URL to redirect to after login. The default implementation
1060+
resolves and returns :attr:`next_page` if set, or
1061+
:setting:`LOGIN_REDIRECT_URL` otherwise.
1062+
10461063
Here's what ``LoginView`` does:
10471064

10481065
* If called via ``GET``, it displays a login form that POSTs to the

tests/auth_tests/test_views.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ def setUpTestData(cls):
5252
cls.u1 = User.objects.create_user(username='testclient', password='password', email='[email protected]')
5353
cls.u3 = User.objects.create_user(username='staff', password='password', email='[email protected]')
5454

55-
def login(self, username='testclient', password='password'):
56-
response = self.client.post('/login/', {
55+
def login(self, username='testclient', password='password', url='/login/'):
56+
response = self.client.post(url, {
5757
'username': username,
5858
'password': password,
5959
})
@@ -726,6 +726,31 @@ def test_login_session_without_hash_session_key(self):
726726
self.login()
727727
self.assertNotEqual(original_session_key, self.client.session.session_key)
728728

729+
def test_login_get_default_redirect_url(self):
730+
response = self.login(url='/login/get_default_redirect_url/')
731+
self.assertRedirects(response, '/custom/', fetch_redirect_response=False)
732+
733+
def test_login_next_page(self):
734+
response = self.login(url='/login/next_page/')
735+
self.assertRedirects(response, '/somewhere/', fetch_redirect_response=False)
736+
737+
def test_login_named_next_page_named(self):
738+
response = self.login(url='/login/next_page/named/')
739+
self.assertRedirects(response, '/password_reset/', fetch_redirect_response=False)
740+
741+
@override_settings(LOGIN_REDIRECT_URL='/custom/')
742+
def test_login_next_page_overrides_login_redirect_url_setting(self):
743+
response = self.login(url='/login/next_page/')
744+
self.assertRedirects(response, '/somewhere/', fetch_redirect_response=False)
745+
746+
def test_login_redirect_url_overrides_next_page(self):
747+
response = self.login(url='/login/next_page/?next=/test/')
748+
self.assertRedirects(response, '/test/', fetch_redirect_response=False)
749+
750+
def test_login_redirect_url_overrides_get_default_redirect_url(self):
751+
response = self.login(url='/login/get_default_redirect_url/?next=/test/')
752+
self.assertRedirects(response, '/test/', fetch_redirect_response=False)
753+
729754

730755
class LoginURLSettings(AuthViewsTestCase):
731756
"""Tests for settings.LOGIN_URL."""

tests/auth_tests/urls.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from django.contrib.auth.decorators import login_required, permission_required
44
from django.contrib.auth.forms import AuthenticationForm
55
from django.contrib.auth.urls import urlpatterns as auth_urlpatterns
6+
from django.contrib.auth.views import LoginView
67
from django.contrib.messages.api import info
78
from django.http import HttpRequest, HttpResponse
89
from django.shortcuts import render
@@ -78,6 +79,11 @@ def login_and_permission_required_exception(request):
7879
pass
7980

8081

82+
class CustomDefaultRedirectURLLoginView(LoginView):
83+
def get_default_redirect_url(self):
84+
return '/custom/'
85+
86+
8187
# special urls for auth test cases
8288
urlpatterns = auth_urlpatterns + [
8389
path('logout/custom_query/', views.LogoutView.as_view(redirect_field_name='follow')),
@@ -149,6 +155,9 @@ def login_and_permission_required_exception(request):
149155
views.LoginView.as_view(redirect_authenticated_user=True)),
150156
path('login/allowed_hosts/',
151157
views.LoginView.as_view(success_url_allowed_hosts={'otherserver'})),
158+
path('login/get_default_redirect_url/', CustomDefaultRedirectURLLoginView.as_view()),
159+
path('login/next_page/', views.LoginView.as_view(next_page='/somewhere/')),
160+
path('login/next_page/named/', views.LoginView.as_view(next_page='password_reset')),
152161

153162
path('permission_required_redirect/', permission_required_redirect),
154163
path('permission_required_exception/', permission_required_exception),

0 commit comments

Comments
 (0)