mirror of https://github.com/django/django.git
Refs #17209 -- Added LoginView and LogoutView class-based views
Thanks Tim Graham for the review.
This commit is contained in:
parent
742ea51413
commit
78963495d0
|
@ -327,7 +327,7 @@ class AdminSite(object):
|
||||||
|
|
||||||
This should *not* assume the user is already logged in.
|
This should *not* assume the user is already logged in.
|
||||||
"""
|
"""
|
||||||
from django.contrib.auth.views import logout
|
from django.contrib.auth.views import LogoutView
|
||||||
defaults = {
|
defaults = {
|
||||||
'extra_context': dict(
|
'extra_context': dict(
|
||||||
self.each_context(request),
|
self.each_context(request),
|
||||||
|
@ -340,7 +340,7 @@ class AdminSite(object):
|
||||||
if self.logout_template is not None:
|
if self.logout_template is not None:
|
||||||
defaults['template_name'] = self.logout_template
|
defaults['template_name'] = self.logout_template
|
||||||
request.current_app = self.name
|
request.current_app = self.name
|
||||||
return logout(request, **defaults)
|
return LogoutView.as_view(**defaults)(request)
|
||||||
|
|
||||||
@never_cache
|
@never_cache
|
||||||
def login(self, request, extra_context=None):
|
def login(self, request, extra_context=None):
|
||||||
|
@ -352,7 +352,7 @@ class AdminSite(object):
|
||||||
index_path = reverse('admin:index', current_app=self.name)
|
index_path = reverse('admin:index', current_app=self.name)
|
||||||
return HttpResponseRedirect(index_path)
|
return HttpResponseRedirect(index_path)
|
||||||
|
|
||||||
from django.contrib.auth.views import login
|
from django.contrib.auth.views import LoginView
|
||||||
# Since this module gets imported in the application's root package,
|
# Since this module gets imported in the application's root package,
|
||||||
# it cannot import models from other applications at the module level,
|
# it cannot import models from other applications at the module level,
|
||||||
# and django.contrib.admin.forms eventually imports User.
|
# and django.contrib.admin.forms eventually imports User.
|
||||||
|
@ -374,7 +374,7 @@ class AdminSite(object):
|
||||||
'template_name': self.login_template or 'admin/login.html',
|
'template_name': self.login_template or 'admin/login.html',
|
||||||
}
|
}
|
||||||
request.current_app = self.name
|
request.current_app = self.name
|
||||||
return login(request, **defaults)
|
return LoginView.as_view(**defaults)(request)
|
||||||
|
|
||||||
def _build_app_dict(self, request, label=None):
|
def _build_app_dict(self, request, label=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -7,8 +7,8 @@ from django.conf.urls import url
|
||||||
from django.contrib.auth import views
|
from django.contrib.auth import views
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^login/$', views.login, name='login'),
|
url(r'^login/$', views.LoginView.as_view(), name='login'),
|
||||||
url(r'^logout/$', views.logout, name='logout'),
|
url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
|
||||||
url(r'^password_change/$', views.password_change, name='password_change'),
|
url(r'^password_change/$', views.password_change, name='password_change'),
|
||||||
url(r'^password_change/done/$', views.password_change_done, name='password_change_done'),
|
url(r'^password_change/done/$', views.password_change_done, name='password_change_done'),
|
||||||
url(r'^password_reset/$', views.password_reset, name='password_reset'),
|
url(r'^password_reset/$', views.password_reset, name='password_reset'),
|
||||||
|
|
|
@ -17,7 +17,10 @@ from django.http import HttpResponseRedirect, QueryDict
|
||||||
from django.shortcuts import resolve_url
|
from django.shortcuts import resolve_url
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.deprecation import RemovedInDjango20Warning
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.utils.deprecation import (
|
||||||
|
RemovedInDjango20Warning, RemovedInDjango21Warning,
|
||||||
|
)
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.http import is_safe_url, urlsafe_base64_decode
|
from django.utils.http import is_safe_url, urlsafe_base64_decode
|
||||||
from django.utils.six.moves.urllib.parse import urlparse, urlunparse
|
from django.utils.six.moves.urllib.parse import urlparse, urlunparse
|
||||||
|
@ -25,6 +28,8 @@ from django.utils.translation import ugettext as _
|
||||||
from django.views.decorators.cache import never_cache
|
from django.views.decorators.cache import never_cache
|
||||||
from django.views.decorators.csrf import csrf_protect
|
from django.views.decorators.csrf import csrf_protect
|
||||||
from django.views.decorators.debug import sensitive_post_parameters
|
from django.views.decorators.debug import sensitive_post_parameters
|
||||||
|
from django.views.generic.base import TemplateView
|
||||||
|
from django.views.generic.edit import FormView
|
||||||
|
|
||||||
|
|
||||||
def deprecate_current_app(func):
|
def deprecate_current_app(func):
|
||||||
|
@ -48,94 +53,128 @@ def deprecate_current_app(func):
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|
||||||
def _get_login_redirect_url(request, redirect_to):
|
class LoginView(FormView):
|
||||||
# Ensure the user-originating redirection URL is safe.
|
|
||||||
if not is_safe_url(url=redirect_to, host=request.get_host()):
|
|
||||||
return resolve_url(settings.LOGIN_REDIRECT_URL)
|
|
||||||
return redirect_to
|
|
||||||
|
|
||||||
|
|
||||||
@deprecate_current_app
|
|
||||||
@sensitive_post_parameters()
|
|
||||||
@csrf_protect
|
|
||||||
@never_cache
|
|
||||||
def login(request, template_name='registration/login.html',
|
|
||||||
redirect_field_name=REDIRECT_FIELD_NAME,
|
|
||||||
authentication_form=AuthenticationForm,
|
|
||||||
extra_context=None, redirect_authenticated_user=False):
|
|
||||||
"""
|
"""
|
||||||
Displays the login form and handles the login action.
|
Displays the login form and handles the login action.
|
||||||
"""
|
"""
|
||||||
redirect_to = request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, ''))
|
form_class = AuthenticationForm
|
||||||
|
authentication_form = None
|
||||||
|
redirect_field_name = REDIRECT_FIELD_NAME
|
||||||
|
template_name = 'registration/login.html'
|
||||||
|
redirect_authenticated_user = False
|
||||||
|
extra_context = None
|
||||||
|
|
||||||
if redirect_authenticated_user and request.user.is_authenticated:
|
@method_decorator(sensitive_post_parameters())
|
||||||
redirect_to = _get_login_redirect_url(request, redirect_to)
|
@method_decorator(csrf_protect)
|
||||||
if redirect_to == request.path:
|
@method_decorator(never_cache)
|
||||||
raise ValueError(
|
def dispatch(self, request, *args, **kwargs):
|
||||||
"Redirection loop for authenticated user detected. Check that "
|
if self.redirect_authenticated_user and self.request.user.is_authenticated:
|
||||||
"your LOGIN_REDIRECT_URL doesn't point to a login page."
|
redirect_to = self.get_success_url()
|
||||||
)
|
if redirect_to == self.request.path:
|
||||||
return HttpResponseRedirect(redirect_to)
|
raise ValueError(
|
||||||
elif request.method == "POST":
|
"Redirection loop for authenticated user detected. Check that "
|
||||||
form = authentication_form(request, data=request.POST)
|
"your LOGIN_REDIRECT_URL doesn't point to a login page."
|
||||||
if form.is_valid():
|
)
|
||||||
auth_login(request, form.get_user())
|
return HttpResponseRedirect(redirect_to)
|
||||||
return HttpResponseRedirect(_get_login_redirect_url(request, redirect_to))
|
return super(LoginView, self).dispatch(request, *args, **kwargs)
|
||||||
else:
|
|
||||||
form = authentication_form(request)
|
|
||||||
|
|
||||||
current_site = get_current_site(request)
|
def get_success_url(self):
|
||||||
|
"""Ensure the user-originating redirection URL is safe."""
|
||||||
|
redirect_to = self.request.POST.get(
|
||||||
|
self.redirect_field_name,
|
||||||
|
self.request.GET.get(self.redirect_field_name, '')
|
||||||
|
)
|
||||||
|
if not is_safe_url(url=redirect_to, host=self.request.get_host()):
|
||||||
|
return resolve_url(settings.LOGIN_REDIRECT_URL)
|
||||||
|
return redirect_to
|
||||||
|
|
||||||
context = {
|
def get_form_class(self):
|
||||||
'form': form,
|
return self.authentication_form or self.form_class
|
||||||
redirect_field_name: redirect_to,
|
|
||||||
'site': current_site,
|
|
||||||
'site_name': current_site.name,
|
|
||||||
}
|
|
||||||
if extra_context is not None:
|
|
||||||
context.update(extra_context)
|
|
||||||
|
|
||||||
return TemplateResponse(request, template_name, context)
|
def form_valid(self, form):
|
||||||
|
"""Security check complete. Log the user in."""
|
||||||
|
auth_login(self.request, form.get_user())
|
||||||
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(LoginView, self).get_context_data(**kwargs)
|
||||||
|
current_site = get_current_site(self.request)
|
||||||
|
context.update({
|
||||||
|
self.redirect_field_name: self.get_success_url(),
|
||||||
|
'site': current_site,
|
||||||
|
'site_name': current_site.name,
|
||||||
|
})
|
||||||
|
if self.extra_context is not None:
|
||||||
|
context.update(self.extra_context)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
@deprecate_current_app
|
@deprecate_current_app
|
||||||
@never_cache
|
def login(request, *args, **kwargs):
|
||||||
def logout(request, next_page=None,
|
warnings.warn(
|
||||||
template_name='registration/logged_out.html',
|
'The login() view is superseded by the class-based LoginView().',
|
||||||
redirect_field_name=REDIRECT_FIELD_NAME,
|
RemovedInDjango21Warning, stacklevel=2
|
||||||
extra_context=None):
|
)
|
||||||
|
return LoginView.as_view(**kwargs)(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class LogoutView(TemplateView):
|
||||||
"""
|
"""
|
||||||
Logs out the user and displays 'You are logged out' message.
|
Logs out the user and displays 'You are logged out' message.
|
||||||
"""
|
"""
|
||||||
auth_logout(request)
|
next_page = None
|
||||||
|
redirect_field_name = REDIRECT_FIELD_NAME
|
||||||
|
template_name = 'registration/logged_out.html'
|
||||||
|
extra_context = None
|
||||||
|
|
||||||
if next_page is not None:
|
@method_decorator(never_cache)
|
||||||
next_page = resolve_url(next_page)
|
def dispatch(self, request, *args, **kwargs):
|
||||||
elif settings.LOGOUT_REDIRECT_URL:
|
auth_logout(request)
|
||||||
next_page = resolve_url(settings.LOGOUT_REDIRECT_URL)
|
next_page = self.get_next_page()
|
||||||
|
if next_page:
|
||||||
|
# Redirect to this page until the session has been cleared.
|
||||||
|
return HttpResponseRedirect(next_page)
|
||||||
|
return super(LogoutView, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
if (redirect_field_name in request.POST or
|
def get_next_page(self):
|
||||||
redirect_field_name in request.GET):
|
if self.next_page is not None:
|
||||||
next_page = request.POST.get(redirect_field_name,
|
next_page = resolve_url(self.next_page)
|
||||||
request.GET.get(redirect_field_name))
|
elif settings.LOGOUT_REDIRECT_URL:
|
||||||
# Security check -- don't allow redirection to a different host.
|
next_page = resolve_url(settings.LOGOUT_REDIRECT_URL)
|
||||||
if not is_safe_url(url=next_page, host=request.get_host()):
|
else:
|
||||||
next_page = request.path
|
next_page = self.next_page
|
||||||
|
|
||||||
if next_page:
|
if (self.redirect_field_name in self.request.POST or
|
||||||
# Redirect to this page until the session has been cleared.
|
self.redirect_field_name in self.request.GET):
|
||||||
return HttpResponseRedirect(next_page)
|
next_page = self.request.POST.get(
|
||||||
|
self.redirect_field_name,
|
||||||
|
self.request.GET.get(self.redirect_field_name)
|
||||||
|
)
|
||||||
|
# Security check -- don't allow redirection to a different host.
|
||||||
|
if not is_safe_url(url=next_page, host=self.request.get_host()):
|
||||||
|
next_page = self.request.path
|
||||||
|
return next_page
|
||||||
|
|
||||||
current_site = get_current_site(request)
|
def get_context_data(self, **kwargs):
|
||||||
context = {
|
context = super(LogoutView, self).get_context_data(**kwargs)
|
||||||
'site': current_site,
|
current_site = get_current_site(self.request)
|
||||||
'site_name': current_site.name,
|
context.update({
|
||||||
'title': _('Logged out')
|
'site': current_site,
|
||||||
}
|
'site_name': current_site.name,
|
||||||
if extra_context is not None:
|
'title': _('Logged out'),
|
||||||
context.update(extra_context)
|
})
|
||||||
|
if self.extra_context is not None:
|
||||||
|
context.update(self.extra_context)
|
||||||
|
return context
|
||||||
|
|
||||||
return TemplateResponse(request, template_name, context)
|
|
||||||
|
@deprecate_current_app
|
||||||
|
def logout(request, *args, **kwargs):
|
||||||
|
warnings.warn(
|
||||||
|
'The logout() view is superseded by the class-based LogoutView().',
|
||||||
|
RemovedInDjango21Warning, stacklevel=2
|
||||||
|
)
|
||||||
|
return LogoutView.as_view(**kwargs)(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@deprecate_current_app
|
@deprecate_current_app
|
||||||
|
|
|
@ -15,6 +15,8 @@ about each item can often be found in the release notes of two versions prior.
|
||||||
See the :ref:`Django 1.11 release notes<deprecated-features-1.11>` for more
|
See the :ref:`Django 1.11 release notes<deprecated-features-1.11>` for more
|
||||||
details on these changes.
|
details on these changes.
|
||||||
|
|
||||||
|
* ``contrib.auth.views.login()`` and ``logout()`` will be removed.
|
||||||
|
|
||||||
.. _deprecation-removed-in-2.0:
|
.. _deprecation-removed-in-2.0:
|
||||||
|
|
||||||
2.0
|
2.0
|
||||||
|
|
|
@ -93,7 +93,7 @@ Fields
|
||||||
if you want to allow inactive users to login. In this case, you'll also
|
if you want to allow inactive users to login. In this case, you'll also
|
||||||
want to customize the
|
want to customize the
|
||||||
:class:`~django.contrib.auth.forms.AuthenticationForm` used by the
|
:class:`~django.contrib.auth.forms.AuthenticationForm` used by the
|
||||||
:func:`~django.contrib.auth.views.login` view as it rejects inactive
|
:class:`~django.contrib.auth.views.LoginView` as it rejects inactive
|
||||||
users. Be aware that the permission-checking methods such as
|
users. Be aware that the permission-checking methods such as
|
||||||
:meth:`~django.contrib.auth.models.User.has_perm` and the
|
:meth:`~django.contrib.auth.models.User.has_perm` and the
|
||||||
authentication in the Django admin all return ``False`` for inactive
|
authentication in the Django admin all return ``False`` for inactive
|
||||||
|
@ -582,7 +582,7 @@ The following backends are available in :mod:`django.contrib.auth.backends`:
|
||||||
|
|
||||||
When using this backend, you'll likely want to customize the
|
When using this backend, you'll likely want to customize the
|
||||||
:class:`~django.contrib.auth.forms.AuthenticationForm` used by the
|
:class:`~django.contrib.auth.forms.AuthenticationForm` used by the
|
||||||
:func:`~django.contrib.auth.views.login` view by overriding the
|
:class:`~django.contrib.auth.views.LoginView` by overriding the
|
||||||
:meth:`~django.contrib.auth.forms.AuthenticationForm.confirm_login_allowed`
|
:meth:`~django.contrib.auth.forms.AuthenticationForm.confirm_login_allowed`
|
||||||
method as it rejects inactive users.
|
method as it rejects inactive users.
|
||||||
|
|
||||||
|
|
|
@ -436,8 +436,8 @@ Here's how Django uses the sites framework:
|
||||||
the current :class:`~django.contrib.sites.models.Site` object if you don't
|
the current :class:`~django.contrib.sites.models.Site` object if you don't
|
||||||
specify a fully-qualified domain.
|
specify a fully-qualified domain.
|
||||||
|
|
||||||
* In the :mod:`authentication framework <django.contrib.auth>`, the
|
* In the :mod:`authentication framework <django.contrib.auth>`,
|
||||||
:func:`django.contrib.auth.views.login` view passes the current
|
:class:`django.contrib.auth.views.LoginView` passes the current
|
||||||
:class:`~django.contrib.sites.models.Site` name to the template as
|
:class:`~django.contrib.sites.models.Site` name to the template as
|
||||||
``{{ site_name }}``.
|
``{{ site_name }}``.
|
||||||
|
|
||||||
|
|
|
@ -2736,8 +2736,8 @@ the URL in two places (``settings`` and URLconf).
|
||||||
|
|
||||||
Default: ``None``
|
Default: ``None``
|
||||||
|
|
||||||
The URL where requests are redirected after a user logs out using the
|
The URL where requests are redirected after a user logs out using
|
||||||
:func:`~django.contrib.auth.views.logout` view (if the view doesn't get a
|
:class:`~django.contrib.auth.views.LogoutView` (if the view doesn't get a
|
||||||
``next_page`` argument).
|
``next_page`` argument).
|
||||||
|
|
||||||
If ``None``, no redirect will be performed and the logout view will be
|
If ``None``, no redirect will be performed and the logout view will be
|
||||||
|
|
|
@ -66,6 +66,10 @@ Minor features
|
||||||
* The default iteration count for the PBKDF2 password hasher is increased by
|
* The default iteration count for the PBKDF2 password hasher is increased by
|
||||||
20%.
|
20%.
|
||||||
|
|
||||||
|
* The :class:`~django.contrib.auth.views.LoginView` and
|
||||||
|
:class:`~django.contrib.auth.views.LogoutView` class-based views supersede the
|
||||||
|
deprecated ``login()`` and ``logout()`` function-based views.
|
||||||
|
|
||||||
:mod:`django.contrib.contenttypes`
|
:mod:`django.contrib.contenttypes`
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -297,4 +301,7 @@ Features deprecated in 1.11
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
* ...
|
* ``contrib.auth``’s ``login()`` and ``logout()`` function-based views are
|
||||||
|
deprecated in favor of new class-based views
|
||||||
|
:class:`~django.contrib.auth.views.LoginView` and
|
||||||
|
:class:`~django.contrib.auth.views.LogoutView`.
|
||||||
|
|
|
@ -503,7 +503,7 @@ The ``login_required`` decorator
|
||||||
|
|
||||||
from django.contrib.auth import views as auth_views
|
from django.contrib.auth import views as auth_views
|
||||||
|
|
||||||
url(r'^accounts/login/$', auth_views.login),
|
url(r'^accounts/login/$', auth_views.Login.as_view()),
|
||||||
|
|
||||||
The :setting:`settings.LOGIN_URL <LOGIN_URL>` also accepts view function
|
The :setting:`settings.LOGIN_URL <LOGIN_URL>` also accepts view function
|
||||||
names and :ref:`named URL patterns <naming-url-patterns>`. This allows you
|
names and :ref:`named URL patterns <naming-url-patterns>`. This allows you
|
||||||
|
@ -957,12 +957,37 @@ implementation details see :ref:`using-the-views`.
|
||||||
|
|
||||||
.. function:: login(request, template_name=`registration/login.html`, redirect_field_name='next', authentication_form=AuthenticationForm, current_app=None, extra_context=None, redirect_authenticated_user=False)
|
.. function:: login(request, template_name=`registration/login.html`, redirect_field_name='next', authentication_form=AuthenticationForm, current_app=None, extra_context=None, redirect_authenticated_user=False)
|
||||||
|
|
||||||
|
.. deprecated:: 1.11
|
||||||
|
|
||||||
|
The ``login`` function-based view should be replaced by the class-based
|
||||||
|
:class:`LoginView`.
|
||||||
|
|
||||||
|
The optional arguments of this view are similar to the class-based
|
||||||
|
``LoginView`` optional attributes. In addition, it has:
|
||||||
|
|
||||||
|
* ``current_app``: A hint indicating which application contains the
|
||||||
|
current view. See the :ref:`namespaced URL resolution strategy
|
||||||
|
<topics-http-reversing-url-namespaces>` for more information.
|
||||||
|
|
||||||
|
.. deprecated:: 1.9
|
||||||
|
|
||||||
|
The ``current_app`` attribute is deprecated and will be removed in
|
||||||
|
Django 2.0. Callers should set ``request.current_app`` instead.
|
||||||
|
|
||||||
|
.. versionadded:: 1.10
|
||||||
|
|
||||||
|
The ``redirect_authenticated_user`` parameter was added.
|
||||||
|
|
||||||
|
.. class:: LoginView
|
||||||
|
|
||||||
|
.. versionadded:: 1.11
|
||||||
|
|
||||||
**URL name:** ``login``
|
**URL name:** ``login``
|
||||||
|
|
||||||
See :doc:`the URL documentation </topics/http/urls>` for details on using
|
See :doc:`the URL documentation </topics/http/urls>` for details on using
|
||||||
named URL patterns.
|
named URL patterns.
|
||||||
|
|
||||||
**Optional arguments:**
|
**Attributes:**
|
||||||
|
|
||||||
* ``template_name``: The name of a template to display for the view used to
|
* ``template_name``: The name of a template to display for the view used to
|
||||||
log the user in. Defaults to :file:`registration/login.html`.
|
log the user in. Defaults to :file:`registration/login.html`.
|
||||||
|
@ -974,10 +999,6 @@ implementation details see :ref:`using-the-views`.
|
||||||
use for authentication. Defaults to
|
use for authentication. Defaults to
|
||||||
:class:`~django.contrib.auth.forms.AuthenticationForm`.
|
:class:`~django.contrib.auth.forms.AuthenticationForm`.
|
||||||
|
|
||||||
* ``current_app``: A hint indicating which application contains the current
|
|
||||||
view. See the :ref:`namespaced URL resolution strategy
|
|
||||||
<topics-http-reversing-url-namespaces>` for more information.
|
|
||||||
|
|
||||||
* ``extra_context``: A dictionary of context data that will be added to the
|
* ``extra_context``: A dictionary of context data that will be added to the
|
||||||
default context data passed to the template.
|
default context data passed to the template.
|
||||||
|
|
||||||
|
@ -985,16 +1006,7 @@ implementation details see :ref:`using-the-views`.
|
||||||
authenticated users accessing the login page will be redirected as if
|
authenticated users accessing the login page will be redirected as if
|
||||||
they had just successfully logged in. Defaults to ``False``.
|
they had just successfully logged in. Defaults to ``False``.
|
||||||
|
|
||||||
.. deprecated:: 1.9
|
Here's what ``LoginView`` does:
|
||||||
|
|
||||||
The ``current_app`` parameter is deprecated and will be removed in
|
|
||||||
Django 2.0. Callers should set ``request.current_app`` instead.
|
|
||||||
|
|
||||||
.. versionadded:: 1.10
|
|
||||||
|
|
||||||
The ``redirect_authenticated_user`` parameter was added.
|
|
||||||
|
|
||||||
Here's what ``django.contrib.auth.views.login`` does:
|
|
||||||
|
|
||||||
* If called via ``GET``, it displays a login form that POSTs to the
|
* If called via ``GET``, it displays a login form that POSTs to the
|
||||||
same URL. More on this in a bit.
|
same URL. More on this in a bit.
|
||||||
|
@ -1030,14 +1042,14 @@ implementation details see :ref:`using-the-views`.
|
||||||
|
|
||||||
If you'd prefer not to call the template :file:`registration/login.html`,
|
If you'd prefer not to call the template :file:`registration/login.html`,
|
||||||
you can pass the ``template_name`` parameter via the extra arguments to
|
you can pass the ``template_name`` parameter via the extra arguments to
|
||||||
the view in your URLconf. For example, this URLconf line would use
|
the ``as_view`` method in your URLconf. For example, this URLconf line would
|
||||||
:file:`myapp/login.html` instead::
|
use :file:`myapp/login.html` instead::
|
||||||
|
|
||||||
url(r'^accounts/login/$', auth_views.login, {'template_name': 'myapp/login.html'}),
|
url(r'^accounts/login/$', auth_views.LoginView.as_view(template_name='myapp/login.html')),
|
||||||
|
|
||||||
You can also specify the name of the ``GET`` field which contains the URL
|
You can also specify the name of the ``GET`` field which contains the URL
|
||||||
to redirect to after login by passing ``redirect_field_name`` to the view.
|
to redirect to after login using ``redirect_field_name``. By default, the
|
||||||
By default, the field is called ``next``.
|
field is called ``next``.
|
||||||
|
|
||||||
Here's a sample :file:`registration/login.html` template you can use as a
|
Here's a sample :file:`registration/login.html` template you can use as a
|
||||||
starting point. It assumes you have a :file:`base.html` template that
|
starting point. It assumes you have a :file:`base.html` template that
|
||||||
|
@ -1084,49 +1096,55 @@ implementation details see :ref:`using-the-views`.
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
If you have customized authentication (see
|
If you have customized authentication (see :doc:`Customizing Authentication
|
||||||
:doc:`Customizing Authentication </topics/auth/customizing>`) you can pass
|
</topics/auth/customizing>`) you can use a custom authentication form by
|
||||||
a custom authentication form to the login view via the
|
setting the ``authentication_form`` attribute. This form must accept a
|
||||||
``authentication_form`` parameter. This form must accept a ``request``
|
``request`` keyword argument in its ``__init__()`` method and provide a
|
||||||
keyword argument in its ``__init__`` method, and provide a ``get_user()``
|
``get_user()`` method which returns the authenticated user object (this
|
||||||
method which returns the authenticated user object (this method is only
|
method is only ever called after successful form validation).
|
||||||
ever called after successful form validation).
|
|
||||||
|
|
||||||
.. _forms documentation: ../forms/
|
|
||||||
.. _site framework docs: ../sites/
|
|
||||||
|
|
||||||
.. function:: logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name='next', current_app=None, extra_context=None)
|
.. function:: logout(request, next_page=None, template_name='registration/logged_out.html', redirect_field_name='next', current_app=None, extra_context=None)
|
||||||
|
|
||||||
|
.. deprecated:: 1.11
|
||||||
|
|
||||||
|
The ``logout`` function-based view should be replaced by the
|
||||||
|
class-based :class:`LogoutView`.
|
||||||
|
|
||||||
|
The optional arguments of this view are similar to the class-based
|
||||||
|
``LogoutView`` optional attributes. In addition, it has:
|
||||||
|
|
||||||
|
* ``current_app``: A hint indicating which application contains the
|
||||||
|
current view. See the :ref:`namespaced URL resolution strategy
|
||||||
|
<topics-http-reversing-url-namespaces>` for more information.
|
||||||
|
|
||||||
|
.. deprecated:: 1.9
|
||||||
|
|
||||||
|
The ``current_app`` attribute is deprecated and will be removed in
|
||||||
|
Django 2.0. Callers should set ``request.current_app`` instead.
|
||||||
|
|
||||||
|
.. class:: LogoutView
|
||||||
|
|
||||||
|
.. versionadded:: 1.11
|
||||||
|
|
||||||
Logs a user out.
|
Logs a user out.
|
||||||
|
|
||||||
**URL name:** ``logout``
|
**URL name:** ``logout``
|
||||||
|
|
||||||
**Optional arguments:**
|
**Attributes:**
|
||||||
|
|
||||||
* ``next_page``: The URL to redirect to after logout. Defaults to
|
* ``next_page``: The URL to redirect to after logout. Defaults to
|
||||||
:setting:`settings.LOGOUT_REDIRECT_URL <LOGOUT_REDIRECT_URL>` if not
|
:setting:`settings.LOGOUT_REDIRECT_URL <LOGOUT_REDIRECT_URL>`.
|
||||||
supplied.
|
|
||||||
|
|
||||||
* ``template_name``: The full name of a template to display after
|
* ``template_name``: The full name of a template to display after
|
||||||
logging the user out. Defaults to
|
logging the user out. Defaults to :file:`registration/logged_out.html`.
|
||||||
:file:`registration/logged_out.html` if no argument is supplied.
|
|
||||||
|
|
||||||
* ``redirect_field_name``: The name of a ``GET`` field containing the
|
* ``redirect_field_name``: The name of a ``GET`` field containing the
|
||||||
URL to redirect to after log out. Defaults to ``next``. Overrides the
|
URL to redirect to after log out. Defaults to ``next``. Overrides the
|
||||||
``next_page`` URL if the given ``GET`` parameter is passed.
|
``next_page`` URL if the given ``GET`` parameter is passed.
|
||||||
|
|
||||||
* ``current_app``: A hint indicating which application contains the current
|
|
||||||
view. See the :ref:`namespaced URL resolution strategy
|
|
||||||
<topics-http-reversing-url-namespaces>` for more information.
|
|
||||||
|
|
||||||
* ``extra_context``: A dictionary of context data that will be added to the
|
* ``extra_context``: A dictionary of context data that will be added to the
|
||||||
default context data passed to the template.
|
default context data passed to the template.
|
||||||
|
|
||||||
.. deprecated:: 1.9
|
|
||||||
|
|
||||||
The ``current_app`` parameter is deprecated and will be removed in
|
|
||||||
Django 2.0. Callers should set ``request.current_app`` instead.
|
|
||||||
|
|
||||||
**Template context:**
|
**Template context:**
|
||||||
|
|
||||||
* ``title``: The string "Logged out", localized.
|
* ``title``: The string "Logged out", localized.
|
||||||
|
|
|
@ -15,7 +15,7 @@ from django.contrib.auth.forms import (
|
||||||
AuthenticationForm, PasswordChangeForm, SetPasswordForm,
|
AuthenticationForm, PasswordChangeForm, SetPasswordForm,
|
||||||
)
|
)
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.views import login as login_view, redirect_to_login
|
from django.contrib.auth.views import LoginView, redirect_to_login
|
||||||
from django.contrib.sessions.middleware import SessionMiddleware
|
from django.contrib.sessions.middleware import SessionMiddleware
|
||||||
from django.contrib.sites.requests import RequestSite
|
from django.contrib.sites.requests import RequestSite
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
|
@ -553,10 +553,10 @@ class LoginTest(AuthViewsTestCase):
|
||||||
# Do a GET to establish a CSRF token
|
# Do a GET to establish a CSRF token
|
||||||
# TestClient isn't used here as we're testing middleware, essentially.
|
# TestClient isn't used here as we're testing middleware, essentially.
|
||||||
req = HttpRequest()
|
req = HttpRequest()
|
||||||
CsrfViewMiddleware().process_view(req, login_view, (), {})
|
CsrfViewMiddleware().process_view(req, LoginView.as_view(), (), {})
|
||||||
# get_token() triggers CSRF token inclusion in the response
|
# get_token() triggers CSRF token inclusion in the response
|
||||||
get_token(req)
|
get_token(req)
|
||||||
resp = login_view(req)
|
resp = LoginView.as_view()(req)
|
||||||
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
||||||
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
|
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
|
||||||
token1 = csrf_cookie.coded_value
|
token1 = csrf_cookie.coded_value
|
||||||
|
@ -569,10 +569,10 @@ class LoginTest(AuthViewsTestCase):
|
||||||
|
|
||||||
# Use POST request to log in
|
# Use POST request to log in
|
||||||
SessionMiddleware().process_request(req)
|
SessionMiddleware().process_request(req)
|
||||||
CsrfViewMiddleware().process_view(req, login_view, (), {})
|
CsrfViewMiddleware().process_view(req, LoginView.as_view(), (), {})
|
||||||
req.META["SERVER_NAME"] = "testserver" # Required to have redirect work in login view
|
req.META["SERVER_NAME"] = "testserver" # Required to have redirect work in login view
|
||||||
req.META["SERVER_PORT"] = 80
|
req.META["SERVER_PORT"] = 80
|
||||||
resp = login_view(req)
|
resp = LoginView.as_view()(req)
|
||||||
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
resp2 = CsrfViewMiddleware().process_response(req, resp)
|
||||||
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
|
csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
|
||||||
token2 = csrf_cookie.coded_value
|
token2 = csrf_cookie.coded_value
|
||||||
|
|
|
@ -61,14 +61,11 @@ def userpage(request):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def custom_request_auth_login(request):
|
|
||||||
return views.login(request, authentication_form=CustomRequestAuthenticationForm)
|
|
||||||
|
|
||||||
# special urls for auth test cases
|
# special urls for auth test cases
|
||||||
urlpatterns = auth_urlpatterns + [
|
urlpatterns = auth_urlpatterns + [
|
||||||
url(r'^logout/custom_query/$', views.logout, dict(redirect_field_name='follow')),
|
url(r'^logout/custom_query/$', views.LogoutView.as_view(redirect_field_name='follow')),
|
||||||
url(r'^logout/next_page/$', views.logout, dict(next_page='/somewhere/')),
|
url(r'^logout/next_page/$', views.LogoutView.as_view(next_page='/somewhere/')),
|
||||||
url(r'^logout/next_page/named/$', views.logout, dict(next_page='password_reset')),
|
url(r'^logout/next_page/named/$', views.LogoutView.as_view(next_page='password_reset')),
|
||||||
url(r'^remote_user/$', remote_user_auth_view),
|
url(r'^remote_user/$', remote_user_auth_view),
|
||||||
url(r'^password_reset_from_email/$', views.password_reset, dict(from_email='staffmember@example.com')),
|
url(r'^password_reset_from_email/$', views.password_reset, dict(from_email='staffmember@example.com')),
|
||||||
url(r'^password_reset_extra_email_context/$', views.password_reset,
|
url(r'^password_reset_extra_email_context/$', views.password_reset,
|
||||||
|
@ -94,10 +91,12 @@ urlpatterns = auth_urlpatterns + [
|
||||||
url(r'^auth_processor_perms/$', auth_processor_perms),
|
url(r'^auth_processor_perms/$', auth_processor_perms),
|
||||||
url(r'^auth_processor_perm_in_perms/$', auth_processor_perm_in_perms),
|
url(r'^auth_processor_perm_in_perms/$', auth_processor_perm_in_perms),
|
||||||
url(r'^auth_processor_messages/$', auth_processor_messages),
|
url(r'^auth_processor_messages/$', auth_processor_messages),
|
||||||
url(r'^custom_request_auth_login/$', custom_request_auth_login),
|
url(r'^custom_request_auth_login/$',
|
||||||
|
views.LoginView.as_view(authentication_form=CustomRequestAuthenticationForm)),
|
||||||
url(r'^userpage/(.+)/$', userpage, name="userpage"),
|
url(r'^userpage/(.+)/$', userpage, name="userpage"),
|
||||||
url(r'^login/redirect_authenticated_user_default/$', views.login),
|
url(r'^login/redirect_authenticated_user_default/$', views.LoginView.as_view()),
|
||||||
url(r'^login/redirect_authenticated_user/$', views.login, dict(redirect_authenticated_user=True)),
|
url(r'^login/redirect_authenticated_user/$',
|
||||||
|
views.LoginView.as_view(redirect_authenticated_user=True)),
|
||||||
|
|
||||||
# This line is only required to render the password reset with is_admin=True
|
# This line is only required to render the password reset with is_admin=True
|
||||||
url(r'^admin/', admin.site.urls),
|
url(r'^admin/', admin.site.urls),
|
||||||
|
|
|
@ -292,5 +292,5 @@ urlpatterns = [
|
||||||
views.BookSigningDetail.as_view()),
|
views.BookSigningDetail.as_view()),
|
||||||
|
|
||||||
# Useful for testing redirects
|
# Useful for testing redirects
|
||||||
url(r'^accounts/login/$', auth_views.login)
|
url(r'^accounts/login/$', auth_views.LoginView.as_view())
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.contrib.auth.views import logout
|
|
||||||
from django.shortcuts import resolve_url
|
from django.shortcuts import resolve_url
|
||||||
from django.test import SimpleTestCase, override_settings
|
from django.test import SimpleTestCase, override_settings
|
||||||
from django.urls import NoReverseMatch, reverse_lazy
|
from django.urls import NoReverseMatch, reverse_lazy
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
from .models import UnimportantThing
|
from .models import UnimportantThing
|
||||||
|
from .urls import some_view
|
||||||
|
|
||||||
|
|
||||||
@override_settings(ROOT_URLCONF='resolve_url.urls')
|
@override_settings(ROOT_URLCONF='resolve_url.urls')
|
||||||
|
@ -53,25 +53,25 @@ class ResolveUrlTests(SimpleTestCase):
|
||||||
Tests that passing a view function to ``resolve_url`` will result in
|
Tests that passing a view function to ``resolve_url`` will result in
|
||||||
the URL path mapping to that view name.
|
the URL path mapping to that view name.
|
||||||
"""
|
"""
|
||||||
resolved_url = resolve_url(logout)
|
resolved_url = resolve_url(some_view)
|
||||||
self.assertEqual('/accounts/logout/', resolved_url)
|
self.assertEqual('/some-url/', resolved_url)
|
||||||
|
|
||||||
def test_lazy_reverse(self):
|
def test_lazy_reverse(self):
|
||||||
"""
|
"""
|
||||||
Tests that passing the result of reverse_lazy is resolved to a real URL
|
Tests that passing the result of reverse_lazy is resolved to a real URL
|
||||||
string.
|
string.
|
||||||
"""
|
"""
|
||||||
resolved_url = resolve_url(reverse_lazy('logout'))
|
resolved_url = resolve_url(reverse_lazy('some-view'))
|
||||||
self.assertIsInstance(resolved_url, six.text_type)
|
self.assertIsInstance(resolved_url, six.text_type)
|
||||||
self.assertEqual('/accounts/logout/', resolved_url)
|
self.assertEqual('/some-url/', resolved_url)
|
||||||
|
|
||||||
def test_valid_view_name(self):
|
def test_valid_view_name(self):
|
||||||
"""
|
"""
|
||||||
Tests that passing a view name to ``resolve_url`` will result in the
|
Tests that passing a view name to ``resolve_url`` will result in the
|
||||||
URL path mapping to that view.
|
URL path mapping to that view.
|
||||||
"""
|
"""
|
||||||
resolved_url = resolve_url('logout')
|
resolved_url = resolve_url('some-view')
|
||||||
self.assertEqual('/accounts/logout/', resolved_url)
|
self.assertEqual('/some-url/', resolved_url)
|
||||||
|
|
||||||
def test_domain(self):
|
def test_domain(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib.auth import views
|
|
||||||
|
|
||||||
|
def some_view(request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^accounts/logout/$', views.logout, name='logout'),
|
url(r'^some-url/$', some_view, name='some-view'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -36,6 +36,6 @@ urlpatterns = [
|
||||||
|
|
||||||
url(r'^accounts/$', RedirectView.as_view(url='login/')),
|
url(r'^accounts/$', RedirectView.as_view(url='login/')),
|
||||||
url(r'^accounts/no_trailing_slash$', RedirectView.as_view(url='login/')),
|
url(r'^accounts/no_trailing_slash$', RedirectView.as_view(url='login/')),
|
||||||
url(r'^accounts/login/$', auth_views.login, {'template_name': 'login.html'}),
|
url(r'^accounts/login/$', auth_views.LoginView.as_view(template_name='login.html')),
|
||||||
url(r'^accounts/logout/$', auth_views.logout),
|
url(r'^accounts/logout/$', auth_views.LogoutView.as_view()),
|
||||||
]
|
]
|
||||||
|
|
|
@ -28,8 +28,8 @@ numeric_days_info_dict = dict(date_based_info_dict, day_format='%d')
|
||||||
date_based_datefield_info_dict = dict(date_based_info_dict, queryset=DateArticle.objects.all())
|
date_based_datefield_info_dict = dict(date_based_info_dict, queryset=DateArticle.objects.all())
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^accounts/login/$', auth_views.login, {'template_name': 'login.html'}),
|
url(r'^accounts/login/$', auth_views.LoginView.as_view(template_name='login.html')),
|
||||||
url(r'^accounts/logout/$', auth_views.logout),
|
url(r'^accounts/logout/$', auth_views.LogoutView.as_view()),
|
||||||
|
|
||||||
# Special URLs for particular regression cases.
|
# Special URLs for particular regression cases.
|
||||||
url('^中文/target/$', views.index_page),
|
url('^中文/target/$', views.index_page),
|
||||||
|
|
Loading…
Reference in New Issue