Browse Source

Refs #17209 -- Added LoginView and LogoutView class-based views

Thanks Tim Graham for the review.
Claude Paroz 8 years ago
parent
commit
78963495d0

+ 4 - 4
django/contrib/admin/sites.py

@@ -327,7 +327,7 @@ class AdminSite(object):
 
         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 = {
             'extra_context': dict(
                 self.each_context(request),
@@ -340,7 +340,7 @@ class AdminSite(object):
         if self.logout_template is not None:
             defaults['template_name'] = self.logout_template
         request.current_app = self.name
-        return logout(request, **defaults)
+        return LogoutView.as_view(**defaults)(request)
 
     @never_cache
     def login(self, request, extra_context=None):
@@ -352,7 +352,7 @@ class AdminSite(object):
             index_path = reverse('admin:index', current_app=self.name)
             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,
         # it cannot import models from other applications at the module level,
         # and django.contrib.admin.forms eventually imports User.
@@ -374,7 +374,7 @@ class AdminSite(object):
             'template_name': self.login_template or 'admin/login.html',
         }
         request.current_app = self.name
-        return login(request, **defaults)
+        return LoginView.as_view(**defaults)(request)
 
     def _build_app_dict(self, request, label=None):
         """

+ 2 - 2
django/contrib/auth/urls.py

@@ -7,8 +7,8 @@ from django.conf.urls import url
 from django.contrib.auth import views
 
 urlpatterns = [
-    url(r'^login/$', views.login, name='login'),
-    url(r'^logout/$', views.logout, name='logout'),
+    url(r'^login/$', views.LoginView.as_view(), name='login'),
+    url(r'^logout/$', views.LogoutView.as_view(), name='logout'),
     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_reset/$', views.password_reset, name='password_reset'),

+ 117 - 78
django/contrib/auth/views.py

@@ -17,7 +17,10 @@ from django.http import HttpResponseRedirect, QueryDict
 from django.shortcuts import resolve_url
 from django.template.response import TemplateResponse
 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.http import is_safe_url, urlsafe_base64_decode
 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.csrf import csrf_protect
 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):
@@ -48,94 +53,128 @@ def deprecate_current_app(func):
     return inner
 
 
-def _get_login_redirect_url(request, redirect_to):
-    # 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):
+class LoginView(FormView):
     """
     Displays the login form and handles the login action.
     """
-    redirect_to = request.POST.get(redirect_field_name, request.GET.get(redirect_field_name, ''))
-
-    if redirect_authenticated_user and request.user.is_authenticated:
-        redirect_to = _get_login_redirect_url(request, redirect_to)
-        if redirect_to == request.path:
-            raise ValueError(
-                "Redirection loop for authenticated user detected. Check that "
-                "your LOGIN_REDIRECT_URL doesn't point to a login page."
-            )
-        return HttpResponseRedirect(redirect_to)
-    elif request.method == "POST":
-        form = authentication_form(request, data=request.POST)
-        if form.is_valid():
-            auth_login(request, form.get_user())
-            return HttpResponseRedirect(_get_login_redirect_url(request, redirect_to))
-    else:
-        form = authentication_form(request)
+    form_class = AuthenticationForm
+    authentication_form = None
+    redirect_field_name = REDIRECT_FIELD_NAME
+    template_name = 'registration/login.html'
+    redirect_authenticated_user = False
+    extra_context = None
+
+    @method_decorator(sensitive_post_parameters())
+    @method_decorator(csrf_protect)
+    @method_decorator(never_cache)
+    def dispatch(self, request, *args, **kwargs):
+        if self.redirect_authenticated_user and self.request.user.is_authenticated:
+            redirect_to = self.get_success_url()
+            if redirect_to == self.request.path:
+                raise ValueError(
+                    "Redirection loop for authenticated user detected. Check that "
+                    "your LOGIN_REDIRECT_URL doesn't point to a login page."
+                )
+            return HttpResponseRedirect(redirect_to)
+        return super(LoginView, self).dispatch(request, *args, **kwargs)
+
+    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
+
+    def get_form_class(self):
+        return self.authentication_form or self.form_class
+
+    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
 
-    current_site = get_current_site(request)
-
-    context = {
-        'form': form,
-        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)
+@deprecate_current_app
+def login(request, *args, **kwargs):
+    warnings.warn(
+        'The login() view is superseded by the class-based LoginView().',
+        RemovedInDjango21Warning, stacklevel=2
+    )
+    return LoginView.as_view(**kwargs)(request, *args, **kwargs)
 
 
-@deprecate_current_app
-@never_cache
-def logout(request, next_page=None,
-           template_name='registration/logged_out.html',
-           redirect_field_name=REDIRECT_FIELD_NAME,
-           extra_context=None):
+class LogoutView(TemplateView):
     """
     Logs out the user and displays 'You are logged out' message.
     """
-    auth_logout(request)
-
-    if next_page is not None:
-        next_page = resolve_url(next_page)
-    elif settings.LOGOUT_REDIRECT_URL:
-        next_page = resolve_url(settings.LOGOUT_REDIRECT_URL)
-
-    if (redirect_field_name in request.POST or
-            redirect_field_name in request.GET):
-        next_page = request.POST.get(redirect_field_name,
-                                     request.GET.get(redirect_field_name))
-        # Security check -- don't allow redirection to a different host.
-        if not is_safe_url(url=next_page, host=request.get_host()):
-            next_page = request.path
-
-    if next_page:
-        # Redirect to this page until the session has been cleared.
-        return HttpResponseRedirect(next_page)
-
-    current_site = get_current_site(request)
-    context = {
-        'site': current_site,
-        'site_name': current_site.name,
-        'title': _('Logged out')
-    }
-    if extra_context is not None:
-        context.update(extra_context)
+    next_page = None
+    redirect_field_name = REDIRECT_FIELD_NAME
+    template_name = 'registration/logged_out.html'
+    extra_context = None
+
+    @method_decorator(never_cache)
+    def dispatch(self, request, *args, **kwargs):
+        auth_logout(request)
+        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)
+
+    def get_next_page(self):
+        if self.next_page is not None:
+            next_page = resolve_url(self.next_page)
+        elif settings.LOGOUT_REDIRECT_URL:
+            next_page = resolve_url(settings.LOGOUT_REDIRECT_URL)
+        else:
+            next_page = self.next_page
 
-    return TemplateResponse(request, template_name, context)
+        if (self.redirect_field_name in self.request.POST or
+                self.redirect_field_name in self.request.GET):
+            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
+
+    def get_context_data(self, **kwargs):
+        context = super(LogoutView, self).get_context_data(**kwargs)
+        current_site = get_current_site(self.request)
+        context.update({
+            'site': current_site,
+            'site_name': current_site.name,
+            'title': _('Logged out'),
+        })
+        if self.extra_context is not None:
+            context.update(self.extra_context)
+        return 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

+ 2 - 0
docs/internals/deprecation.txt

@@ -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
 details on these changes.
 
+* ``contrib.auth.views.login()`` and ``logout()`` will be removed.
+
 .. _deprecation-removed-in-2.0:
 
 2.0

+ 2 - 2
docs/ref/contrib/auth.txt

@@ -93,7 +93,7 @@ Fields
         if you want to allow inactive users to login. In this case, you'll also
         want to customize 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
         :meth:`~django.contrib.auth.models.User.has_perm` and the
         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
    :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`
    method as it rejects inactive users.
 

+ 2 - 2
docs/ref/contrib/sites.txt

@@ -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
   specify a fully-qualified domain.
 
-* In the :mod:`authentication framework <django.contrib.auth>`, the
-  :func:`django.contrib.auth.views.login` view passes the current
+* In the :mod:`authentication framework <django.contrib.auth>`,
+  :class:`django.contrib.auth.views.LoginView` passes the current
   :class:`~django.contrib.sites.models.Site` name to the template as
   ``{{ site_name }}``.
 

+ 2 - 2
docs/ref/settings.txt

@@ -2736,8 +2736,8 @@ the URL in two places (``settings`` and URLconf).
 
 Default: ``None``
 
-The URL where requests are redirected after a user logs out using the
-:func:`~django.contrib.auth.views.logout` view (if the view doesn't get a
+The URL where requests are redirected after a user logs out using
+:class:`~django.contrib.auth.views.LogoutView` (if the view doesn't get a
 ``next_page`` argument).
 
 If ``None``, no redirect will be performed and the logout view will be

+ 8 - 1
docs/releases/1.11.txt

@@ -66,6 +66,10 @@ Minor features
 * The default iteration count for the PBKDF2 password hasher is increased by
   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`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -297,4 +301,7 @@ Features deprecated in 1.11
 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`.

+ 63 - 45
docs/topics/auth/default.txt

@@ -503,7 +503,7 @@ The ``login_required`` decorator
 
         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
     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)
 
+    .. 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``
 
     See :doc:`the URL documentation </topics/http/urls>` for details on using
     named URL patterns.
 
-    **Optional arguments:**
+    **Attributes:**
 
     * ``template_name``: The name of a template to display for the view used to
       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
       :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
       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
       they had just successfully logged in. Defaults to ``False``.
 
-    .. deprecated:: 1.9
-
-        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:
+    Here's what ``LoginView`` does:
 
     * If called via ``GET``, it displays a login form that POSTs to the
       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`,
     you can pass the ``template_name`` parameter via the extra arguments to
-    the view in your URLconf. For example, this URLconf line would use
-    :file:`myapp/login.html` instead::
+    the ``as_view`` method in your URLconf. For example, this URLconf line would
+    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
-    to redirect to after login by passing ``redirect_field_name`` to the view.
-    By default, the field is called ``next``.
+    to redirect to after login using ``redirect_field_name``. By default, the
+    field is called ``next``.
 
     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
@@ -1084,49 +1096,55 @@ implementation details see :ref:`using-the-views`.
 
         {% endblock %}
 
-    If you have customized authentication (see
-    :doc:`Customizing Authentication </topics/auth/customizing>`) you can pass
-    a custom authentication form to the login view via the
-    ``authentication_form`` parameter. This form must accept a ``request``
-    keyword argument in its ``__init__`` method, and provide a ``get_user()``
-    method which returns the authenticated user object (this method is only
-    ever called after successful form validation).
-
-    .. _forms documentation: ../forms/
-    .. _site framework docs: ../sites/
+    If you have customized authentication (see :doc:`Customizing Authentication
+    </topics/auth/customizing>`) you can use a custom authentication form by
+    setting the ``authentication_form`` attribute. This form must accept a
+    ``request`` keyword argument in its ``__init__()`` method and provide a
+    ``get_user()`` method which returns the authenticated user object (this
+    method is only ever called after successful form validation).
 
 .. 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.
 
     **URL name:** ``logout``
 
-    **Optional arguments:**
+    **Attributes:**
 
     * ``next_page``: The URL to redirect to after logout. Defaults to
-      :setting:`settings.LOGOUT_REDIRECT_URL <LOGOUT_REDIRECT_URL>` if not
-      supplied.
+      :setting:`settings.LOGOUT_REDIRECT_URL <LOGOUT_REDIRECT_URL>`.
 
     * ``template_name``: The full name of a template to display after
-      logging the user out. Defaults to
-      :file:`registration/logged_out.html` if no argument is supplied.
+      logging the user out. Defaults to :file:`registration/logged_out.html`.
 
     * ``redirect_field_name``: The name of a ``GET`` field containing the
       URL to redirect to after log out. Defaults to ``next``. Overrides the
       ``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
       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:**
 
     * ``title``: The string "Logged out", localized.

+ 5 - 5
tests/auth_tests/test_views.py

@@ -15,7 +15,7 @@ from django.contrib.auth.forms import (
     AuthenticationForm, PasswordChangeForm, SetPasswordForm,
 )
 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.sites.requests import RequestSite
 from django.core import mail
@@ -553,10 +553,10 @@ class LoginTest(AuthViewsTestCase):
         # Do a GET to establish a CSRF token
         # TestClient isn't used here as we're testing middleware, essentially.
         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(req)
-        resp = login_view(req)
+        resp = LoginView.as_view()(req)
         resp2 = CsrfViewMiddleware().process_response(req, resp)
         csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
         token1 = csrf_cookie.coded_value
@@ -569,10 +569,10 @@ class LoginTest(AuthViewsTestCase):
 
         # Use POST request to log in
         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_PORT"] = 80
-        resp = login_view(req)
+        resp = LoginView.as_view()(req)
         resp2 = CsrfViewMiddleware().process_response(req, resp)
         csrf_cookie = resp2.cookies.get(settings.CSRF_COOKIE_NAME, None)
         token2 = csrf_cookie.coded_value

+ 8 - 9
tests/auth_tests/urls.py

@@ -61,14 +61,11 @@ def userpage(request):
     pass
 
 
-def custom_request_auth_login(request):
-    return views.login(request, authentication_form=CustomRequestAuthenticationForm)
-
 # special urls for auth test cases
 urlpatterns = auth_urlpatterns + [
-    url(r'^logout/custom_query/$', views.logout, dict(redirect_field_name='follow')),
-    url(r'^logout/next_page/$', views.logout, dict(next_page='/somewhere/')),
-    url(r'^logout/next_page/named/$', views.logout, dict(next_page='password_reset')),
+    url(r'^logout/custom_query/$', views.LogoutView.as_view(redirect_field_name='follow')),
+    url(r'^logout/next_page/$', views.LogoutView.as_view(next_page='/somewhere/')),
+    url(r'^logout/next_page/named/$', views.LogoutView.as_view(next_page='password_reset')),
     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_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_perm_in_perms/$', auth_processor_perm_in_perms),
     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'^login/redirect_authenticated_user_default/$', views.login),
-    url(r'^login/redirect_authenticated_user/$', views.login, dict(redirect_authenticated_user=True)),
+    url(r'^login/redirect_authenticated_user_default/$', views.LoginView.as_view()),
+    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
     url(r'^admin/', admin.site.urls),

+ 1 - 1
tests/generic_views/urls.py

@@ -292,5 +292,5 @@ urlpatterns = [
         views.BookSigningDetail.as_view()),
 
     # Useful for testing redirects
-    url(r'^accounts/login/$', auth_views.login)
+    url(r'^accounts/login/$', auth_views.LoginView.as_view())
 ]

+ 7 - 7
tests/resolve_url/tests.py

@@ -1,12 +1,12 @@
 from __future__ import unicode_literals
 
-from django.contrib.auth.views import logout
 from django.shortcuts import resolve_url
 from django.test import SimpleTestCase, override_settings
 from django.urls import NoReverseMatch, reverse_lazy
 from django.utils import six
 
 from .models import UnimportantThing
+from .urls import some_view
 
 
 @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
         the URL path mapping to that view name.
         """
-        resolved_url = resolve_url(logout)
-        self.assertEqual('/accounts/logout/', resolved_url)
+        resolved_url = resolve_url(some_view)
+        self.assertEqual('/some-url/', resolved_url)
 
     def test_lazy_reverse(self):
         """
         Tests that passing the result of reverse_lazy is resolved to a real URL
         string.
         """
-        resolved_url = resolve_url(reverse_lazy('logout'))
+        resolved_url = resolve_url(reverse_lazy('some-view'))
         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):
         """
         Tests that passing a view name to ``resolve_url`` will result in the
         URL path mapping to that view.
         """
-        resolved_url = resolve_url('logout')
-        self.assertEqual('/accounts/logout/', resolved_url)
+        resolved_url = resolve_url('some-view')
+        self.assertEqual('/some-url/', resolved_url)
 
     def test_domain(self):
         """

+ 6 - 2
tests/resolve_url/urls.py

@@ -1,6 +1,10 @@
 from django.conf.urls import url
-from django.contrib.auth import views
+
+
+def some_view(request):
+    pass
+
 
 urlpatterns = [
-    url(r'^accounts/logout/$', views.logout, name='logout'),
+    url(r'^some-url/$', some_view, name='some-view'),
 ]

+ 2 - 2
tests/test_client/urls.py

@@ -36,6 +36,6 @@ urlpatterns = [
 
     url(r'^accounts/$', 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/logout/$', auth_views.logout),
+    url(r'^accounts/login/$', auth_views.LoginView.as_view(template_name='login.html')),
+    url(r'^accounts/logout/$', auth_views.LogoutView.as_view()),
 ]

+ 2 - 2
tests/view_tests/generic_urls.py

@@ -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())
 
 urlpatterns = [
-    url(r'^accounts/login/$', auth_views.login, {'template_name': 'login.html'}),
-    url(r'^accounts/logout/$', auth_views.logout),
+    url(r'^accounts/login/$', auth_views.LoginView.as_view(template_name='login.html')),
+    url(r'^accounts/logout/$', auth_views.LogoutView.as_view()),
 
     # Special URLs for particular regression cases.
     url('^中文/target/$', views.index_page),