diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index aaa56f12962..707428b0557 100644 --- a/django/contrib/admin/sites.py +++ b/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): """ diff --git a/django/contrib/auth/urls.py b/django/contrib/auth/urls.py index ab2ba9e9e08..008ed092a6e 100644 --- a/django/contrib/auth/urls.py +++ b/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'), diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index 3cf7c36b8be..38a0f658c11 100644 --- a/django/contrib/auth/views.py +++ b/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, '')) + 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: - 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) + @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) - 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 = { - '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) + def get_form_class(self): + return self.authentication_form or self.form_class - 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 -@never_cache -def logout(request, next_page=None, - template_name='registration/logged_out.html', - redirect_field_name=REDIRECT_FIELD_NAME, - extra_context=None): +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) + + +class LogoutView(TemplateView): """ 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: - next_page = resolve_url(next_page) - elif settings.LOGOUT_REDIRECT_URL: - next_page = resolve_url(settings.LOGOUT_REDIRECT_URL) + @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) - 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 + 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 - if next_page: - # Redirect to this page until the session has been cleared. - return HttpResponseRedirect(next_page) + 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 - 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) + 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 - 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 diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 6f113488322..48c56af7856 100644 --- a/docs/internals/deprecation.txt +++ b/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` for more details on these changes. +* ``contrib.auth.views.login()`` and ``logout()`` will be removed. + .. _deprecation-removed-in-2.0: 2.0 diff --git a/docs/ref/contrib/auth.txt b/docs/ref/contrib/auth.txt index 5836cb329ec..91e3fbd1ba4 100644 --- a/docs/ref/contrib/auth.txt +++ b/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. diff --git a/docs/ref/contrib/sites.txt b/docs/ref/contrib/sites.txt index 82fa1e41d5e..668744a9c2d 100644 --- a/docs/ref/contrib/sites.txt +++ b/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 `, the - :func:`django.contrib.auth.views.login` view passes the current +* In the :mod:`authentication framework `, + :class:`django.contrib.auth.views.LoginView` passes the current :class:`~django.contrib.sites.models.Site` name to the template as ``{{ site_name }}``. diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index ab4f9c6efe7..f995f4c21d2 100644 --- a/docs/ref/settings.txt +++ b/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 diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt index dc8b657a13b..ca54d0b223a 100644 --- a/docs/releases/1.11.txt +++ b/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`. diff --git a/docs/topics/auth/default.txt b/docs/topics/auth/default.txt index f0cbf07b142..14f872f4eec 100644 --- a/docs/topics/auth/default.txt +++ b/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 ` also accepts view function names and :ref:`named 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 + ` 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 ` 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 - ` 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 `) 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 + `) 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 + ` 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 ` if not - supplied. + :setting:`settings.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 - ` 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. diff --git a/tests/auth_tests/test_views.py b/tests/auth_tests/test_views.py index 5b4b877c470..bd01c67d701 100644 --- a/tests/auth_tests/test_views.py +++ b/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 diff --git a/tests/auth_tests/urls.py b/tests/auth_tests/urls.py index 2b70c59a515..c6dd632e0a1 100644 --- a/tests/auth_tests/urls.py +++ b/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), diff --git a/tests/generic_views/urls.py b/tests/generic_views/urls.py index e95f9c01c7a..288383e032d 100644 --- a/tests/generic_views/urls.py +++ b/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()) ] diff --git a/tests/resolve_url/tests.py b/tests/resolve_url/tests.py index b909f68abee..ecccfa6c9ea 100644 --- a/tests/resolve_url/tests.py +++ b/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): """ diff --git a/tests/resolve_url/urls.py b/tests/resolve_url/urls.py index e78b833927b..43429d6f2a1 100644 --- a/tests/resolve_url/urls.py +++ b/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'), ] diff --git a/tests/test_client/urls.py b/tests/test_client/urls.py index a7b0ed310d7..ca8d5a8fc2e 100644 --- a/tests/test_client/urls.py +++ b/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()), ] diff --git a/tests/view_tests/generic_urls.py b/tests/view_tests/generic_urls.py index ee1c7d123b4..4ca2c8927ad 100644 --- a/tests/view_tests/generic_urls.py +++ b/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),