From bb00b2839980145984c487410fc65fbc0c94be3d Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Fri, 10 Sep 2010 19:38:57 +0000 Subject: [PATCH] Added login_url argument to login_required decorator. Thanks mhlakhani and ericflo for the report and patch. git-svn-id: http://code.djangoproject.com/svn/django/trunk@13723 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/auth/decorators.py | 3 ++- django/contrib/auth/tests/decorators.py | 30 ++++++++++++++++++++----- django/contrib/auth/tests/urls.py | 4 ++++ django/contrib/auth/tests/views.py | 29 +++++++++--------------- docs/topics/auth.txt | 17 +++++++++++--- 5 files changed, 55 insertions(+), 28 deletions(-) diff --git a/django/contrib/auth/decorators.py b/django/contrib/auth/decorators.py index 09dcf42e42..7d7a0cddb7 100644 --- a/django/contrib/auth/decorators.py +++ b/django/contrib/auth/decorators.py @@ -30,13 +30,14 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE return decorator -def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME): +def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None): """ Decorator for views that checks that the user is logged in, redirecting to the log-in page if necessary. """ actual_decorator = user_passes_test( lambda u: u.is_authenticated(), + login_url=login_url, redirect_field_name=redirect_field_name ) if function: diff --git a/django/contrib/auth/tests/decorators.py b/django/contrib/auth/tests/decorators.py index 7efd9d8ccf..0240a76eb7 100644 --- a/django/contrib/auth/tests/decorators.py +++ b/django/contrib/auth/tests/decorators.py @@ -1,12 +1,12 @@ -from unittest import TestCase - from django.contrib.auth.decorators import login_required +from django.contrib.auth.tests.views import AuthViewsTestCase - -class LoginRequiredTestCase(TestCase): +class LoginRequiredTestCase(AuthViewsTestCase): """ Tests the login_required decorators """ + urls = 'django.contrib.auth.tests.urls' + def testCallable(self): """ Check that login_required is assignable to callable objects. @@ -22,4 +22,24 @@ class LoginRequiredTestCase(TestCase): """ def normal_view(request): pass - login_required(normal_view) \ No newline at end of file + login_required(normal_view) + + def testLoginRequired(self, view_url='/login_required/', login_url='/login/'): + """ + Check that login_required works on a simple view wrapped in a + login_required decorator. + """ + response = self.client.get(view_url) + self.assertEqual(response.status_code, 302) + self.assert_(login_url in response['Location']) + self.login() + response = self.client.get(view_url) + self.assertEqual(response.status_code, 200) + + def testLoginRequiredNextUrl(self): + """ + Check that login_required works on a simple view wrapped in a + login_required decorator with a login_url set. + """ + self.testLoginRequired(view_url='/login_required_login_url/', + login_url='/somewhere/') \ No newline at end of file diff --git a/django/contrib/auth/tests/urls.py b/django/contrib/auth/tests/urls.py index f94b8daa7f..c6252ed2de 100644 --- a/django/contrib/auth/tests/urls.py +++ b/django/contrib/auth/tests/urls.py @@ -1,5 +1,7 @@ from django.conf.urls.defaults import patterns from django.contrib.auth.urls import urlpatterns +from django.contrib.auth.views import password_reset +from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.template import Template, RequestContext @@ -14,5 +16,7 @@ urlpatterns += patterns('', (r'^logout/custom_query/$', 'django.contrib.auth.views.logout', dict(redirect_field_name='follow')), (r'^logout/next_page/$', 'django.contrib.auth.views.logout', dict(next_page='/somewhere/')), (r'^remote_user/$', remote_user_auth_view), + (r'^login_required/$', login_required(password_reset)), + (r'^login_required_login_url/$', login_required(password_reset, login_url='/somewhere/')), ) diff --git a/django/contrib/auth/tests/views.py b/django/contrib/auth/tests/views.py index d894e6dafd..6b8efad894 100644 --- a/django/contrib/auth/tests/views.py +++ b/django/contrib/auth/tests/views.py @@ -36,6 +36,16 @@ class AuthViewsTestCase(TestCase): settings.LANGUAGE_CODE = self.old_LANGUAGE_CODE settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS + def login(self, password='password'): + response = self.client.post('/login/', { + 'username': 'testclient', + 'password': password + } + ) + self.assertEquals(response.status_code, 302) + self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL)) + self.assert_(SESSION_KEY in self.client.session) + class PasswordResetTest(AuthViewsTestCase): def test_email_not_found(self): @@ -118,15 +128,6 @@ class PasswordResetTest(AuthViewsTestCase): class ChangePasswordTest(AuthViewsTestCase): - def login(self, password='password'): - response = self.client.post('/login/', { - 'username': 'testclient', - 'password': password - } - ) - self.assertEquals(response.status_code, 302) - self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL)) - def fail_login(self, password='password'): response = self.client.post('/login/', { 'username': 'testclient', @@ -228,16 +229,6 @@ class LoginTest(AuthViewsTestCase): class LogoutTest(AuthViewsTestCase): urls = 'django.contrib.auth.tests.urls' - def login(self, password='password'): - response = self.client.post('/login/', { - 'username': 'testclient', - 'password': password - } - ) - self.assertEquals(response.status_code, 302) - self.assert_(response['Location'].endswith(settings.LOGIN_REDIRECT_URL)) - self.assert_(SESSION_KEY in self.client.session) - def confirm_logged_out(self): self.assert_(SESSION_KEY not in self.client.session) diff --git a/docs/topics/auth.txt b/docs/topics/auth.txt index 4a6e30fc00..00b95ba085 100644 --- a/docs/topics/auth.txt +++ b/docs/topics/auth.txt @@ -713,6 +713,17 @@ The login_required decorator def my_view(request): ... + .. versionadded:: 1.3 + + :func:`~django.contrib.auth.decorators.login_required` also takes an + optional ``login_url`` parameter. Example:: + + from django.contrib.auth.decorators import login_required + + @login_required(login_url='/accounts/login/') + def my_view(request): + ... + :func:`~django.contrib.auth.decorators.login_required` does the following: * If the user isn't logged in, redirect to @@ -726,9 +737,9 @@ The login_required decorator * If the user is logged in, execute the view normally. The view code is free to assume the user is logged in. -Note that you'll need to map the appropriate Django view to -:setting:`settings.LOGIN_URL `. For example, using the defaults, add -the following line to your URLconf:: +Note that if you don't specify the ``login_url`` parameter, you'll need to map +the appropriate Django view to :setting:`settings.LOGIN_URL `. For +example, using the defaults, add the following line to your URLconf:: (r'^accounts/login/$', 'django.contrib.auth.views.login'),