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
This commit is contained in:
Jannis Leidel 2010-09-10 19:38:57 +00:00
parent 27265f70a1
commit bb00b28399
5 changed files with 55 additions and 28 deletions

View File

@ -30,13 +30,14 @@ def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIE
return decorator 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 Decorator for views that checks that the user is logged in, redirecting
to the log-in page if necessary. to the log-in page if necessary.
""" """
actual_decorator = user_passes_test( actual_decorator = user_passes_test(
lambda u: u.is_authenticated(), lambda u: u.is_authenticated(),
login_url=login_url,
redirect_field_name=redirect_field_name redirect_field_name=redirect_field_name
) )
if function: if function:

View File

@ -1,12 +1,12 @@
from unittest import TestCase
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.tests.views import AuthViewsTestCase
class LoginRequiredTestCase(AuthViewsTestCase):
class LoginRequiredTestCase(TestCase):
""" """
Tests the login_required decorators Tests the login_required decorators
""" """
urls = 'django.contrib.auth.tests.urls'
def testCallable(self): def testCallable(self):
""" """
Check that login_required is assignable to callable objects. Check that login_required is assignable to callable objects.
@ -23,3 +23,23 @@ class LoginRequiredTestCase(TestCase):
def normal_view(request): def normal_view(request):
pass pass
login_required(normal_view) 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/')

View File

@ -1,5 +1,7 @@
from django.conf.urls.defaults import patterns from django.conf.urls.defaults import patterns
from django.contrib.auth.urls import urlpatterns 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.http import HttpResponse
from django.template import Template, RequestContext 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/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'^logout/next_page/$', 'django.contrib.auth.views.logout', dict(next_page='/somewhere/')),
(r'^remote_user/$', remote_user_auth_view), (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/')),
) )

View File

@ -36,6 +36,16 @@ class AuthViewsTestCase(TestCase):
settings.LANGUAGE_CODE = self.old_LANGUAGE_CODE settings.LANGUAGE_CODE = self.old_LANGUAGE_CODE
settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS 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): class PasswordResetTest(AuthViewsTestCase):
def test_email_not_found(self): def test_email_not_found(self):
@ -118,15 +128,6 @@ class PasswordResetTest(AuthViewsTestCase):
class ChangePasswordTest(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'): def fail_login(self, password='password'):
response = self.client.post('/login/', { response = self.client.post('/login/', {
'username': 'testclient', 'username': 'testclient',
@ -228,16 +229,6 @@ class LoginTest(AuthViewsTestCase):
class LogoutTest(AuthViewsTestCase): class LogoutTest(AuthViewsTestCase):
urls = 'django.contrib.auth.tests.urls' 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): def confirm_logged_out(self):
self.assert_(SESSION_KEY not in self.client.session) self.assert_(SESSION_KEY not in self.client.session)

View File

@ -713,6 +713,17 @@ The login_required decorator
def my_view(request): 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: :func:`~django.contrib.auth.decorators.login_required` does the following:
* If the user isn't logged in, redirect to * 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 * If the user is logged in, execute the view normally. The view code is
free to assume the user is logged in. free to assume the user is logged in.
Note that you'll need to map the appropriate Django view to Note that if you don't specify the ``login_url`` parameter, you'll need to map
:setting:`settings.LOGIN_URL <LOGIN_URL>`. For example, using the defaults, add the appropriate Django view to :setting:`settings.LOGIN_URL <LOGIN_URL>`. For
the following line to your URLconf:: example, using the defaults, add the following line to your URLconf::
(r'^accounts/login/$', 'django.contrib.auth.views.login'), (r'^accounts/login/$', 'django.contrib.auth.views.login'),