Fixed #5394 -- REDIRECT_FIELD_NAME is now configurable. Thanks, Petr Marhoun, DavidReynolds and effbot

git-svn-id: http://code.djangoproject.com/svn/django/trunk@6206 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2007-09-14 19:25:37 +00:00
parent 0d52d2b2f9
commit 55d6aebfec
6 changed files with 58 additions and 10 deletions

View File

@ -2,7 +2,7 @@ from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from urllib import quote from urllib import quote
def user_passes_test(test_func, login_url=None): def user_passes_test(test_func, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
""" """
Decorator for views that checks that the user passes the given test, Decorator for views that checks that the user passes the given test,
redirecting to the log-in page if necessary. The test should be a callable redirecting to the log-in page if necessary. The test should be a callable
@ -15,20 +15,25 @@ def user_passes_test(test_func, login_url=None):
def _checklogin(request, *args, **kwargs): def _checklogin(request, *args, **kwargs):
if test_func(request.user): if test_func(request.user):
return view_func(request, *args, **kwargs) return view_func(request, *args, **kwargs)
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, quote(request.get_full_path()))) return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, quote(request.get_full_path())))
_checklogin.__doc__ = view_func.__doc__ _checklogin.__doc__ = view_func.__doc__
_checklogin.__dict__ = view_func.__dict__ _checklogin.__dict__ = view_func.__dict__
return _checklogin return _checklogin
return _dec return _dec
login_required = user_passes_test(lambda u: u.is_authenticated()) def login_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME):
login_required.__doc__ = (
""" """
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(
lambda u: u.is_authenticated(),
redirect_field_name=redirect_field_name
) )
if function:
return actual_decorator(function)
return actual_decorator
def permission_required(perm, login_url=None): def permission_required(perm, login_url=None):
""" """

View File

@ -9,10 +9,10 @@ from django.contrib.auth.decorators import login_required
from django.contrib.auth import REDIRECT_FIELD_NAME from django.contrib.auth import REDIRECT_FIELD_NAME
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
def login(request, template_name='registration/login.html'): def login(request, template_name='registration/login.html', redirect_field_name=REDIRECT_FIELD_NAME):
"Displays the login form and handles the login action." "Displays the login form and handles the login action."
manipulator = AuthenticationForm(request) manipulator = AuthenticationForm(request)
redirect_to = request.REQUEST.get(REDIRECT_FIELD_NAME, '') redirect_to = request.REQUEST.get(redirect_field_name, '')
if request.POST: if request.POST:
errors = manipulator.get_validation_errors(request.POST) errors = manipulator.get_validation_errors(request.POST)
if not errors: if not errors:
@ -35,7 +35,7 @@ def login(request, template_name='registration/login.html'):
return render_to_response(template_name, { return render_to_response(template_name, {
'form': oldforms.FormWrapper(manipulator, request.POST, errors), 'form': oldforms.FormWrapper(manipulator, request.POST, errors),
REDIRECT_FIELD_NAME: redirect_to, redirect_field_name: redirect_to,
'site_name': current_site.name, 'site_name': current_site.name,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@ -56,12 +56,12 @@ def logout_then_login(request, login_url=None):
login_url = settings.LOGIN_URL login_url = settings.LOGIN_URL
return logout(request, login_url) return logout(request, login_url)
def redirect_to_login(next, login_url=None): def redirect_to_login(next, login_url=None, redirect_field_name=REDIRECT_FIELD_NAME):
"Redirects the user to the login page, passing the given 'next' page" "Redirects the user to the login page, passing the given 'next' page"
if not login_url: if not login_url:
from django.conf import settings from django.conf import settings
login_url = settings.LOGIN_URL login_url = settings.LOGIN_URL
return HttpResponseRedirect('%s?%s=%s' % (login_url, REDIRECT_FIELD_NAME, next)) return HttpResponseRedirect('%s?%s=%s' % (login_url, redirect_field_name, next))
def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html', def password_reset(request, is_admin_site=False, template_name='registration/password_reset_form.html',
email_template_name='registration/password_reset_email.html'): email_template_name='registration/password_reset_email.html'):

View File

@ -402,11 +402,29 @@ introduced in Python 2.4::
def my_view(request): def my_view(request):
# ... # ...
In the Django development version, ``login_required`` also takes an optional
``redirect_field_name`` parameter. Example::
from django.contrib.auth.decorators import login_required
def my_view(request):
# ...
my_view = login_required(redirect_field_name='redirect_to')(my_view)
Again, an equivalent example of the more compact decorator syntax introduced in Python 2.4::
from django.contrib.auth.decorators import login_required
@login_required(redirect_field_name='redirect_to')
def my_view(request):
# ...
``login_required`` does the following: ``login_required`` does the following:
* If the user isn't logged in, redirect to ``settings.LOGIN_URL`` * If the user isn't logged in, redirect to ``settings.LOGIN_URL``
(``/accounts/login/`` by default), passing the current absolute URL (``/accounts/login/`` by default), passing the current absolute URL
in the query string as ``next``. For example: in the query string as ``next`` or the value of ``redirect_field_name``.
For example:
``/accounts/login/?next=/polls/3/``. ``/accounts/login/?next=/polls/3/``.
* 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.

View File

@ -250,6 +250,22 @@ class ClientTest(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['user'].username, 'testclient') self.assertEqual(response.context['user'].username, 'testclient')
def test_view_with_login_and_custom_redirect(self):
"Request a page that is protected with @login_required(redirect_field_name='redirect_to')"
# Get the page without logging in. Should result in 302.
response = self.client.get('/test_client/login_protected_view_custom_redirect/')
self.assertRedirects(response, 'http://testserver/accounts/login/?redirect_to=/test_client/login_protected_view_custom_redirect/')
# Log in
login = self.client.login(username='testclient', password='password')
self.failUnless(login, 'Could not log in')
# Request a page that requires a login
response = self.client.get('/test_client/login_protected_view_custom_redirect/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['user'].username, 'testclient')
def test_view_with_bad_login(self): def test_view_with_bad_login(self):
"Request a page that is protected with @login, but use bad credentials" "Request a page that is protected with @login, but use bad credentials"

View File

@ -13,6 +13,7 @@ urlpatterns = patterns('',
(r'^form_view/$', views.form_view), (r'^form_view/$', views.form_view),
(r'^form_view_with_template/$', views.form_view_with_template), (r'^form_view_with_template/$', views.form_view_with_template),
(r'^login_protected_view/$', views.login_protected_view), (r'^login_protected_view/$', views.login_protected_view),
(r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect),
(r'^session_view/$', views.session_view), (r'^session_view/$', views.session_view),
(r'^broken_view/$', views.broken_view), (r'^broken_view/$', views.broken_view),
(r'^mail_sending_view/$', views.mail_sending_view), (r'^mail_sending_view/$', views.mail_sending_view),

View File

@ -122,6 +122,14 @@ def login_protected_view(request):
return HttpResponse(t.render(c)) return HttpResponse(t.render(c))
login_protected_view = login_required(login_protected_view) login_protected_view = login_required(login_protected_view)
def login_protected_view_changed_redirect(request):
"A simple view that is login protected with a custom redirect field set"
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
c = Context({'user': request.user})
return HttpResponse(t.render(c))
login_protected_view_changed_redirect = login_required(redirect_field_name="redirect_to")(login_protected_view_changed_redirect)
def session_view(request): def session_view(request):
"A view that modifies the session" "A view that modifies the session"
request.session['tobacconist'] = 'hovercraft' request.session['tobacconist'] = 'hovercraft'