Browse Source

Deprecated TEMPLATE_LOADERS.

Aymeric Augustin 10 years ago
parent
commit
cf0fd65ed4

+ 26 - 0
django/contrib/auth/tests/settings.py

@@ -0,0 +1,26 @@
+import os
+
+from django.utils._os import upath
+
+
+AUTH_MIDDLEWARE_CLASSES = (
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+)
+
+AUTH_TEMPLATES = [{
+    'BACKEND': 'django.template.backends.django.DjangoTemplates',
+    'DIRS': [os.path.join(os.path.dirname(upath(__file__)), 'templates')],
+    'APP_DIRS': True,
+    'OPTIONS': {
+        'context_processors': (
+            'django.contrib.auth.context_processors.auth',
+            'django.template.context_processors.debug',
+            'django.template.context_processors.i18n',
+            'django.template.context_processors.media',
+            'django.template.context_processors.static',
+            'django.template.context_processors.tz',
+            'django.contrib.messages.context_processors.messages',
+        ),
+    },
+}]

+ 7 - 25
django/contrib/auth/tests/test_context_processors.py

@@ -1,13 +1,12 @@
-import os
-
 from django.contrib.auth import authenticate
-from django.contrib.auth.tests.utils import skipIfCustomUser
 from django.contrib.auth.models import User, Permission
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.auth.context_processors import PermWrapper, PermLookupDict
 from django.db.models import Q
 from django.test import TestCase, override_settings
-from django.utils._os import upath
+
+from .settings import AUTH_MIDDLEWARE_CLASSES, AUTH_TEMPLATES
+from .utils import skipIfCustomUser
 
 
 class MockUser(object):
@@ -61,17 +60,10 @@ class PermWrapperTests(TestCase):
 
 @skipIfCustomUser
 @override_settings(
-    TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader',),
-    TEMPLATE_DIRS=(
-        os.path.join(os.path.dirname(upath(__file__)), 'templates'),
-    ),
-    TEMPLATE_CONTEXT_PROCESSORS=(
-        'django.contrib.auth.context_processors.auth',
-        'django.contrib.messages.context_processors.messages'
-    ),
+    PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
     ROOT_URLCONF='django.contrib.auth.tests.urls',
+    TEMPLATES=AUTH_TEMPLATES,
     USE_TZ=False,                           # required for loading the fixture
-    PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
 )
 class AuthContextProcessorTests(TestCase):
     """
@@ -79,12 +71,7 @@ class AuthContextProcessorTests(TestCase):
     """
     fixtures = ['context-processors-users.xml']
 
-    @override_settings(
-        MIDDLEWARE_CLASSES=(
-            'django.contrib.sessions.middleware.SessionMiddleware',
-            'django.contrib.auth.middleware.AuthenticationMiddleware',
-        ),
-    )
+    @override_settings(MIDDLEWARE_CLASSES=AUTH_MIDDLEWARE_CLASSES)
     def test_session_not_accessed(self):
         """
         Tests that the session is not accessed simply by including
@@ -93,12 +80,7 @@ class AuthContextProcessorTests(TestCase):
         response = self.client.get('/auth_processor_no_attr_access/')
         self.assertContains(response, "Session not accessed")
 
-    @override_settings(
-        MIDDLEWARE_CLASSES=(
-            'django.contrib.sessions.middleware.SessionMiddleware',
-            'django.contrib.auth.middleware.AuthenticationMiddleware',
-        ),
-    )
+    @override_settings(MIDDLEWARE_CLASSES=AUTH_MIDDLEWARE_CLASSES)
     def test_session_is_accessed(self):
         """
         Tests that the session is accessed if the auth context processor

+ 29 - 34
django/contrib/auth/tests/test_forms.py

@@ -1,6 +1,5 @@
 from __future__ import unicode_literals
 
-import os
 import re
 
 from django import forms
@@ -8,17 +7,18 @@ from django.contrib.auth.models import User
 from django.contrib.auth.forms import (UserCreationForm, AuthenticationForm,
     PasswordChangeForm, SetPasswordForm, UserChangeForm, PasswordResetForm,
     ReadOnlyPasswordHashField, ReadOnlyPasswordHashWidget)
-from django.contrib.auth.tests.utils import skipIfCustomUser
 from django.core import mail
 from django.core.mail import EmailMultiAlternatives
 from django.forms.fields import Field, CharField
 from django.test import TestCase, override_settings
 from django.utils.encoding import force_text
-from django.utils._os import upath
 from django.utils import translation
 from django.utils.text import capfirst
 from django.utils.translation import ugettext as _
 
+from .settings import AUTH_TEMPLATES
+from .utils import skipIfCustomUser
+
 
 @skipIfCustomUser
 @override_settings(USE_TZ=False, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
@@ -360,10 +360,7 @@ class UserChangeFormTest(TestCase):
 @skipIfCustomUser
 @override_settings(
     PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
-    TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader',),
-    TEMPLATE_DIRS=(
-        os.path.join(os.path.dirname(upath(__file__)), 'templates'),
-    ),
+    TEMPLATES=AUTH_TEMPLATES,
     USE_TZ=False,
 )
 class PasswordResetFormTest(TestCase):
@@ -416,33 +413,31 @@ class PasswordResetFormTest(TestCase):
         self.assertEqual(mail.outbox[0].subject, 'Custom password reset on example.com')
 
     def test_custom_email_constructor(self):
-        template_path = os.path.join(os.path.dirname(__file__), 'templates')
-        with self.settings(TEMPLATE_DIRS=(template_path,)):
-            data = {'email': 'testclient@example.com'}
-
-            class CustomEmailPasswordResetForm(PasswordResetForm):
-                def send_mail(self, subject_template_name, email_template_name,
-                              context, from_email, to_email,
-                              html_email_template_name=None):
-                    EmailMultiAlternatives(
-                        "Forgot your password?",
-                        "Sorry to hear you forgot your password.",
-                        None, [to_email],
-                        ['site_monitor@example.com'],
-                        headers={'Reply-To': 'webmaster@example.com'},
-                        alternatives=[("Really sorry to hear you forgot your password.",
-                                       "text/html")]).send()
-
-            form = CustomEmailPasswordResetForm(data)
-            self.assertTrue(form.is_valid())
-            # Since we're not providing a request object, we must provide a
-            # domain_override to prevent the save operation from failing in the
-            # potential case where contrib.sites is not installed. Refs #16412.
-            form.save(domain_override='example.com')
-            self.assertEqual(len(mail.outbox), 1)
-            self.assertEqual(mail.outbox[0].subject, 'Forgot your password?')
-            self.assertEqual(mail.outbox[0].bcc, ['site_monitor@example.com'])
-            self.assertEqual(mail.outbox[0].content_subtype, "plain")
+        data = {'email': 'testclient@example.com'}
+
+        class CustomEmailPasswordResetForm(PasswordResetForm):
+            def send_mail(self, subject_template_name, email_template_name,
+                          context, from_email, to_email,
+                          html_email_template_name=None):
+                EmailMultiAlternatives(
+                    "Forgot your password?",
+                    "Sorry to hear you forgot your password.",
+                    None, [to_email],
+                    ['site_monitor@example.com'],
+                    headers={'Reply-To': 'webmaster@example.com'},
+                    alternatives=[("Really sorry to hear you forgot your password.",
+                                   "text/html")]).send()
+
+        form = CustomEmailPasswordResetForm(data)
+        self.assertTrue(form.is_valid())
+        # Since we're not providing a request object, we must provide a
+        # domain_override to prevent the save operation from failing in the
+        # potential case where contrib.sites is not installed. Refs #16412.
+        form.save(domain_override='example.com')
+        self.assertEqual(len(mail.outbox), 1)
+        self.assertEqual(mail.outbox[0].subject, 'Forgot your password?')
+        self.assertEqual(mail.outbox[0].bcc, ['site_monitor@example.com'])
+        self.assertEqual(mail.outbox[0].content_subtype, "plain")
 
     def test_preserve_username_case(self):
         """

+ 9 - 13
django/contrib/auth/tests/test_views.py

@@ -1,14 +1,17 @@
 from importlib import import_module
 import itertools
-import os
 import re
 import warnings
 
 from django.apps import apps
-from django.conf import global_settings, settings
+from django.conf import settings
 from django.contrib.sites.requests import RequestSite
 from django.contrib.admin.models import LogEntry
+from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME
+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
 from django.core import mail
 from django.core.urlresolvers import reverse, NoReverseMatch
 from django.http import QueryDict, HttpRequest
@@ -16,19 +19,15 @@ from django.utils.encoding import force_text
 from django.utils.http import urlquote
 from django.utils.six.moves.urllib.parse import urlparse, ParseResult
 from django.utils.translation import LANGUAGE_SESSION_KEY
-from django.utils._os import upath
 from django.test import TestCase, override_settings
 from django.test.utils import patch_logger
 from django.middleware.csrf import CsrfViewMiddleware
 from django.contrib.sessions.middleware import SessionMiddleware
 
-from django.contrib.auth import SESSION_KEY, REDIRECT_FIELD_NAME
-from django.contrib.auth.forms import (AuthenticationForm, PasswordChangeForm,
-                SetPasswordForm)
 # Needed so model is installed when tests are run independently:
-from django.contrib.auth.tests.custom_user import CustomUser  # NOQA
-from django.contrib.auth.tests.utils import skipIfCustomUser
-from django.contrib.auth.views import login as login_view
+from .custom_user import CustomUser  # NOQA
+from .settings import AUTH_TEMPLATES
+from .utils import skipIfCustomUser
 
 
 @override_settings(
@@ -36,10 +35,7 @@ from django.contrib.auth.views import login as login_view
         ('en', 'English'),
     ),
     LANGUAGE_CODE='en',
-    TEMPLATE_LOADERS=global_settings.TEMPLATE_LOADERS,
-    TEMPLATE_DIRS=(
-        os.path.join(os.path.dirname(upath(__file__)), 'templates'),
-    ),
+    TEMPLATES=AUTH_TEMPLATES,
     USE_TZ=False,
     PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
     ROOT_URLCONF='django.contrib.auth.tests.urls',

+ 3 - 3
django/views/debug.py

@@ -781,7 +781,7 @@ TECHNICAL_500_TEMPLATE = ("""
         {% endfor %}
         </ul>
     {% else %}
-        <p>Django couldn't find any templates because your <code>TEMPLATE_LOADERS</code> setting is empty!</p>
+        <p>Django couldn't find any templates because your <code>'loaders'</code> option is empty!</p>
     {% endif %}
 </div>
 {% endif %}
@@ -900,7 +900,7 @@ Installed Middleware:
 {% for loader in loader_debug_info %}Using loader {{ loader.loader }}:
 {% for t in loader.templates %}{{ t.name }} ({{ t.status }})
 {% endfor %}{% endfor %}
-{% else %}Django couldn't find any templates because your TEMPLATE_LOADERS setting is empty!
+{% else %}Django couldn't find any templates because your 'loaders' option is empty!
 {% endif %}
 {% endif %}{% if template_info %}
 Template error:
@@ -1091,7 +1091,7 @@ Installed Middleware:
 {% for loader in loader_debug_info %}Using loader {{ loader.loader }}:
 {% for t in loader.templates %}{{ t.name }} ({{ t.status }})
 {% endfor %}{% endfor %}
-{% else %}Django couldn't find any templates because your TEMPLATE_LOADERS setting is empty!
+{% else %}Django couldn't find any templates because your 'loaders' option is empty!
 {% endif %}
 {% endif %}{% if template_info %}
 Template error:

+ 2 - 2
docs/howto/deployment/checklist.txt

@@ -178,8 +178,8 @@ processing time.
 
 This helps a lot on virtualized hosts with limited network performance.
 
-:setting:`TEMPLATE_LOADERS`
----------------------------
+:setting:`TEMPLATES`
+--------------------
 
 Enabling the cached template loader often improves performance drastically, as
 it avoids compiling each template every time it needs to be rendered. See the

+ 1 - 0
docs/internals/deprecation.txt

@@ -90,6 +90,7 @@ details on these changes.
 * The following settings will be removed:
 
   * ``ALLOWED_INCLUDE_ROOTS``
+  * ``TEMPLATE_LOADERS``
   * ``TEMPLATE_STRING_IF_INVALID``
 
 * The backwards compatibility alias ``django.template.loader.BaseLoader`` will

+ 7 - 6
docs/intro/tutorial03.txt

@@ -314,12 +314,13 @@ creating a template that the view can use.
 First, create a directory called ``templates`` in your ``polls`` directory.
 Django will look for templates in there.
 
-Django's :setting:`TEMPLATE_LOADERS` setting contains a list of callables that
-know how to import templates from various sources. One of the defaults is
-:class:`django.template.loaders.app_directories.Loader` which looks for a
-"templates" subdirectory in each of the :setting:`INSTALLED_APPS` - this is how
-Django knows to find the polls templates even though we didn't modify
-:setting:`TEMPLATE_DIRS`, as we did in :ref:`Tutorial 2
+Your project's :setting:`TEMPLATES` setting describes how Django will load and
+render templates. The default settings file configures a ``DjangoTemplates``
+backend whose :setting:`APP_DIRS <TEMPLATES-APP_DIRS>` option is set to
+``True``. By convention ``DjangoTemplates`` looks for a "templates"
+subdirectory in each of the :setting:`INSTALLED_APPS`. This is how Django
+knows to find the polls templates even though we didn't modify the
+:setting:`DIRS <TEMPLATES-DIRS>` option, as we did in :ref:`Tutorial 2
 <ref-customizing-your-projects-templates>`.
 
 .. admonition:: Organizing templates

+ 3 - 2
docs/ref/contrib/admin/index.txt

@@ -2337,8 +2337,9 @@ directory.
 
 In order to override one or more of them, first create an ``admin`` directory
 in your project's ``templates`` directory. This can be any of the directories
-you specified in :setting:`TEMPLATE_DIRS`. If you have customized the
-:setting:`TEMPLATE_LOADERS` setting, be sure
+you specified in the :setting:`DIRS <TEMPLATES-DIRS>` option of the
+``DjangoTemplates`` backend in the :setting:`TEMPLATES` setting. If you have
+customized the ``'loaders'`` option, be sure
 ``'django.template.loaders.filesystem.Loader'`` appears before
 ``'django.template.loaders.app_directories.Loader'`` so that your custom
 templates will be found by the template loading system before those that are

+ 3 - 3
docs/ref/contrib/sitemaps.txt

@@ -34,9 +34,9 @@ To install the sitemap app, follow these steps:
 1. Add ``'django.contrib.sitemaps'`` to your :setting:`INSTALLED_APPS`
    setting.
 
-2. Make sure ``'django.template.loaders.app_directories.Loader'``
-   is in your :setting:`TEMPLATE_LOADERS` setting. It's in there by default,
-   so you'll only need to change this if you've changed that setting.
+2. Make sure your :setting:`TEMPLATES` setting contains a ``DjangoTemplates``
+   backend whose ``APP_DIRS`` options is set to ``True``. It's in there by
+   default, so you'll only need to change this if you've changed that setting.
 
 3. Make sure you've installed the
    :mod:`sites framework <django.contrib.sites>`.

+ 5 - 0
docs/ref/settings.txt

@@ -2439,6 +2439,11 @@ Default::
      ('django.template.loaders.filesystem.Loader',
       'django.template.loaders.app_directories.Loader')
 
+.. deprecated:: 1.8
+
+    Set the ``'loaders'`` option in the :setting:`OPTIONS <TEMPLATES-OPTIONS>`
+    of a ``DjangoTemplates`` backend instead.
+
 A tuple of template loader classes, specified as strings. Each ``Loader`` class
 knows how to import templates from a particular source. Optionally, a tuple can be
 used instead of a string. The first item in the tuple should be the ``Loader``’s

+ 46 - 20
docs/ref/templates/api.txt

@@ -734,9 +734,10 @@ with a few other template loaders, which know how to load templates from other
 sources.
 
 Some of these other loaders are disabled by default, but you can activate them
-by editing your :setting:`TEMPLATE_LOADERS` setting. :setting:`TEMPLATE_LOADERS`
-should be a tuple of strings or tuples, where each represents a template loader
-class. Here are the template loaders that come with Django:
+by adding a ``'loaders'`` option to your ``DjangoTemplates`` backend in the
+:setting:`TEMPLATES` setting. ``'loaders'`` should be a list of strings or
+tuples, where each represents a template loader class. Here are the template
+loaders that come with Django:
 
 .. currentmodule:: django.template.loaders
 
@@ -744,8 +745,16 @@ class. Here are the template loaders that come with Django:
 
 .. class:: filesystem.Loader
 
-    Loads templates from the filesystem, according to :setting:`TEMPLATE_DIRS`.
-    This loader is enabled by default.
+    Loads templates from the filesystem, according to
+    :setting:`DIRS <TEMPLATES-DIRS>`.
+
+    This loader is enabled by default. However it won't find any templates
+    until you set :setting:`DIRS <TEMPLATES-DIRS>` to a non-empty list::
+
+        TEMPLATES = [{
+            'BACKEND': 'django.template.backends.django.DjangoTemplates',
+            'DIRS': [os.path.join(BASE_DIR, 'templates')],
+        }]
 
 ``django.template.loaders.app_directories.Loader``
 
@@ -782,7 +791,14 @@ class. Here are the template loaders that come with Django:
     it caches a list of which :setting:`INSTALLED_APPS` packages have a
     ``templates`` subdirectory.
 
-    This loader is enabled by default.
+    This loader is enabled if and only if :setting:`APP_DIRS
+    <TEMPLATES-APP_DIRS>` is set::
+
+        TEMPLATES = [{
+            'BACKEND': 'django.template.backends.django.DjangoTemplates',
+            'APP_DIRS': True,
+        }]
+
 
 ``django.template.loaders.eggs.Loader``
 
@@ -810,12 +826,18 @@ class. Here are the template loaders that come with Django:
     For example, to enable template caching with the ``filesystem`` and
     ``app_directories`` template loaders you might use the following settings::
 
-        TEMPLATE_LOADERS = (
-            ('django.template.loaders.cached.Loader', (
-                'django.template.loaders.filesystem.Loader',
-                'django.template.loaders.app_directories.Loader',
-            )),
-        )
+        TEMPLATES = [{
+            'BACKEND': 'django.template.backends.django.DjangoTemplates',
+            'DIRS': [os.path.join(BASE_DIR, 'templates')],
+            'OPTIONS': {
+                'loaders': [
+                    ('django.template.loaders.cached.Loader', (
+                        'django.template.loaders.filesystem.Loader',
+                        'django.template.loaders.app_directories.Loader',
+                    )),
+                ],
+            },
+        }]
 
     .. note::
 
@@ -838,17 +860,21 @@ class. Here are the template loaders that come with Django:
 
     This loader takes a dictionary of templates as its first argument::
 
-        TEMPLATE_LOADERS = (
-            ('django.template.loaders.locmem.Loader', {
-                'index.html': 'content here',
-            }),
-        )
+        TEMPLATES = [{
+            'BACKEND': 'django.template.backends.django.DjangoTemplates',
+            'OPTIONS': {
+                'loaders': [
+                    ('django.template.loaders.locmem.Loader', {
+                        'index.html': 'content here',
+                    }),
+                ],
+            },
+        }]
 
     This loader is disabled by default.
 
-Django uses the template loaders in order according to the
-:setting:`TEMPLATE_LOADERS` setting. It uses each loader until a loader finds a
-match.
+Django uses the template loaders in order according to the ``'loaders'``
+option. It uses each loader until a loader finds a match.
 
 .. currentmodule:: django.template
 

+ 3 - 3
docs/releases/1.2.txt

@@ -267,9 +267,9 @@ opposed to functions, the only method available until Django 1.1.
 All the template loaders :ref:`shipped with Django <template-loaders>` have
 been ported to the new API but they still implement the function-based API and
 the template core machinery still accepts function-based loaders (builtin or
-third party) so there is no immediate need to modify your
-:setting:`TEMPLATE_LOADERS` setting in existing projects, things will keep
-working if you leave it untouched up to and including the Django 1.3 release.
+third party) so there is no immediate need to modify your ``TEMPLATE_LOADERS``
+setting in existing projects, things will keep working if you leave it
+untouched up to and including the Django 1.3 release.
 
 If you have developed your own custom template loaders we suggest to consider
 porting them to a class-based implementation because the code for backwards

+ 2 - 1
docs/releases/1.8.txt

@@ -917,7 +917,7 @@ Miscellaneous
   session store always fetches the most current session data.
 
 * Private APIs ``override_template_loaders`` and ``override_with_test_loader``
-  in ``django.test.utils`` were removed. Override ``TEMPLATE_LOADERS`` with
+  in ``django.test.utils`` were removed. Override ``TEMPLATES`` with
   ``override_settings`` instead.
 
 * Warnings from the MySQL database backend are no longer converted to
@@ -1021,6 +1021,7 @@ As a consequence of the multiple template engines refactor, several settings
 are deprecated in favor of :setting:`TEMPLATES`:
 
 * ``ALLOWED_INCLUDE_ROOTS``
+* ``TEMPLATE_LOADERS``
 * ``TEMPLATE_STRING_IF_INVALID``
 
 ``django.core.context_processors``

+ 3 - 4
docs/topics/class-based-views/generic-display.txt

@@ -136,10 +136,9 @@ bit is just the lowercased version of the model's name.
 
 .. note::
 
-    Thus, when (for example) the
-    :class:`django.template.loaders.app_directories.Loader` template loader is
-    enabled in :setting:`TEMPLATE_LOADERS`, a template location could be:
-    /path/to/project/books/templates/books/publisher_list.html
+    Thus, when (for example) the ``APP_DIRS`` option of a ``DjangoTemplates``
+    backend is set to True in :setting:`TEMPLATES`, a template location could
+    be: /path/to/project/books/templates/books/publisher_list.html
 
 This template will be rendered against a context containing a variable called
 ``object_list`` that contains all the publisher objects. A very simple template

+ 1 - 2
docs/topics/testing/tools.txt

@@ -1303,8 +1303,7 @@ Django itself uses this signal to reset various data:
 Overridden settings              Data reset
 ================================ ========================
 USE_TZ, TIME_ZONE                Databases timezone
-TEMPLATE_CONTEXT_PROCESSORS      Context processors cache
-TEMPLATE_LOADERS                 Template loaders cache
+TEMPLATES                        Template engines
 SERIALIZATION_MODULES            Serializers cache
 LOCALE_PATHS, LANGUAGE_CODE      Default translation and loaded translations
 MEDIA_ROOT, DEFAULT_FILE_STORAGE Default file storage

+ 4 - 4
tests/settings_tests/tests.py

@@ -431,14 +431,14 @@ class IsOverriddenTest(TestCase):
             s = Settings('fake_settings_module')
 
             self.assertTrue(s.is_overridden('SECRET_KEY'))
-            self.assertFalse(s.is_overridden('TEMPLATE_LOADERS'))
+            self.assertFalse(s.is_overridden('ALLOWED_HOSTS'))
         finally:
             del sys.modules['fake_settings_module']
 
     def test_override(self):
-        self.assertFalse(settings.is_overridden('TEMPLATE_LOADERS'))
-        with override_settings(TEMPLATE_LOADERS=[]):
-            self.assertTrue(settings.is_overridden('TEMPLATE_LOADERS'))
+        self.assertFalse(settings.is_overridden('ALLOWED_HOSTS'))
+        with override_settings(ALLOWED_HOSTS=[]):
+            self.assertTrue(settings.is_overridden('ALLOWED_HOSTS'))
 
 
 class TestTupleSettings(unittest.TestCase):

+ 64 - 50
tests/template_tests/test_loaders.py

@@ -16,7 +16,7 @@ except ImportError:
 
 
 from django.template import TemplateDoesNotExist, Context
-from django.template.loaders.eggs import Loader as EggLoader
+from django.template.loaders import cached, eggs
 from django.template.engine import Engine
 from django.template import loader
 from django.test import SimpleTestCase, override_settings
@@ -26,6 +26,11 @@ from django.utils._os import upath
 from django.utils.six import StringIO
 
 
+TEMPLATES_DIR = os.path.join(os.path.dirname(upath(__file__)), 'templates')
+
+GLOBAL_TEMPLATES_DIR = os.path.join(os.path.dirname(os.path.dirname(upath(__file__))), 'templates')
+
+
 # Mock classes and objects for pkg_resources functions.
 class MockLoader(object):
     pass
@@ -48,7 +53,10 @@ def create_egg(name, resources):
 
 @unittest.skipUnless(pkg_resources, 'setuptools is not installed')
 class EggLoaderTest(SimpleTestCase):
+
     def setUp(self):
+        self.loader = eggs.Loader(Engine.get_default())
+
         # Defined here b/c at module scope we may not have pkg_resources
         class MockProvider(pkg_resources.NullProvider):
             def __init__(self, module):
@@ -81,70 +89,64 @@ class EggLoaderTest(SimpleTestCase):
     @override_settings(INSTALLED_APPS=['egg_empty'])
     def test_empty(self):
         "Loading any template on an empty egg should fail"
-        egg_loader = EggLoader(Engine.get_default())
-        self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "not-existing.html")
+        with self.assertRaises(TemplateDoesNotExist):
+            self.loader.load_template_source("not-existing.html")
 
     @override_settings(INSTALLED_APPS=['egg_1'])
     def test_non_existing(self):
         "Template loading fails if the template is not in the egg"
-        egg_loader = EggLoader(Engine.get_default())
-        self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "not-existing.html")
+        with self.assertRaises(TemplateDoesNotExist):
+            self.loader.load_template_source("not-existing.html")
 
     @override_settings(INSTALLED_APPS=['egg_1'])
     def test_existing(self):
         "A template can be loaded from an egg"
-        egg_loader = EggLoader(Engine.get_default())
-        contents, template_name = egg_loader.load_template_source("y.html")
+        contents, template_name = self.loader.load_template_source("y.html")
         self.assertEqual(contents, "y")
         self.assertEqual(template_name, "egg:egg_1:templates/y.html")
 
     def test_not_installed(self):
         "Loading an existent template from an egg not included in any app should fail"
-        egg_loader = EggLoader(Engine.get_default())
-        self.assertRaises(TemplateDoesNotExist, egg_loader.load_template_source, "y.html")
+        with self.assertRaises(TemplateDoesNotExist):
+            self.loader.load_template_source("y.html")
 
 
-@override_settings(
-    TEMPLATE_LOADERS=(
-        ('django.template.loaders.cached.Loader', (
-            'django.template.loaders.filesystem.Loader',
-        )),
-    )
-)
 class CachedLoader(SimpleTestCase):
+
+    def setUp(self):
+        self.loader = cached.Loader(Engine.get_default(), [
+            'django.template.loaders.filesystem.Loader',
+        ])
+
     def test_templatedir_caching(self):
         "Check that the template directories form part of the template cache key. Refs #13573"
-        template_loader = Engine.get_default().template_loaders[0]
-
         # Retrieve a template specifying a template directory to check
-        t1, name = template_loader.find_template('test.html', (os.path.join(os.path.dirname(upath(__file__)), 'templates', 'first'),))
+        t1, name = self.loader.find_template('test.html', (os.path.join(TEMPLATES_DIR, 'first'),))
         # Now retrieve the same template name, but from a different directory
-        t2, name = template_loader.find_template('test.html', (os.path.join(os.path.dirname(upath(__file__)), 'templates', 'second'),))
+        t2, name = self.loader.find_template('test.html', (os.path.join(TEMPLATES_DIR, 'second'),))
 
         # The two templates should not have the same content
         self.assertNotEqual(t1.render(Context({})), t2.render(Context({})))
 
     def test_missing_template_is_cached(self):
         "#19949 -- Check that the missing template is cached."
-        template_loader = Engine.get_default().template_loaders[0]
-        # Empty cache, which may be filled from previous tests.
-        template_loader.reset()
         # Check that 'missing.html' isn't already in cache before 'missing.html' is loaded
-        self.assertRaises(KeyError, lambda: template_loader.template_cache["missing.html"])
+        with self.assertRaises(KeyError):
+            self.loader.template_cache["missing.html"]
         # Try to load it, it should fail
-        self.assertRaises(TemplateDoesNotExist, template_loader.load_template, "missing.html")
+        with self.assertRaises(TemplateDoesNotExist):
+            self.loader.load_template("missing.html")
         # Verify that the fact that the missing template, which hasn't been found, has actually
         # been cached:
-        self.assertEqual(template_loader.template_cache.get("missing.html"),
-                         TemplateDoesNotExist,
+        cached_miss = self.loader.template_cache["missing.html"]
+        self.assertEqual(cached_miss, TemplateDoesNotExist,
                          "Cached template loader doesn't cache file lookup misses. It should.")
 
 
-@override_settings(
-    TEMPLATE_DIRS=(
-        os.path.join(os.path.dirname(upath(__file__)), 'templates'),
-    )
-)
+@override_settings(TEMPLATES=[{
+    'BACKEND': 'django.template.backends.django.DjangoTemplates',
+    'DIRS': [TEMPLATES_DIR],
+}])
 class RenderToStringTest(SimpleTestCase):
     def test_basic(self):
         self.assertEqual(loader.render_to_string('test_context.html'), 'obj:\n')
@@ -164,11 +166,10 @@ class RenderToStringTest(SimpleTestCase):
             loader.select_template, [])
 
 
-@override_settings(
-    TEMPLATE_DIRS=(
-        os.path.join(os.path.dirname(upath(__file__)), 'templates'),
-    )
-)
+@override_settings(TEMPLATES=[{
+    'BACKEND': 'django.template.backends.django.DjangoTemplates',
+    'DIRS': [TEMPLATES_DIR],
+}])
 class DeprecatedRenderToStringTest(IgnorePendingDeprecationWarningsMixin, SimpleTestCase):
 
     def test_existing_context_kept_clean(self):
@@ -191,7 +192,10 @@ class DeprecatedRenderToStringTest(IgnorePendingDeprecationWarningsMixin, Simple
             loader.render_to_string('test_context_stack.html', context_instance=Context()).strip())
 
 
-class TemplateDirsOverrideTest(IgnorePendingDeprecationWarningsMixin, unittest.TestCase):
+@override_settings(TEMPLATES=[{
+    'BACKEND': 'django.template.backends.django.DjangoTemplates',
+}])
+class TemplateDirsOverrideTest(IgnorePendingDeprecationWarningsMixin, SimpleTestCase):
 
     dirs_tuple = (os.path.join(os.path.dirname(upath(__file__)), 'other_templates'),)
     dirs_list = list(dirs_tuple)
@@ -212,14 +216,18 @@ class TemplateDirsOverrideTest(IgnorePendingDeprecationWarningsMixin, unittest.T
             self.assertEqual(template.render(Context({})), 'spam eggs\n')
 
 
-@override_settings(
-    TEMPLATE_LOADERS=(
-        ('django.template.loaders.cached.Loader', (
-            'django.template.loaders.filesystem.Loader',
-            'django.template.loaders.app_directories.Loader',
-        )),
-    )
-)
+@override_settings(TEMPLATES=[{
+    'BACKEND': 'django.template.backends.django.DjangoTemplates',
+    'DIRS': [GLOBAL_TEMPLATES_DIR],
+    'OPTIONS': {
+        'loaders': [
+            ('django.template.loaders.cached.Loader', [
+                'django.template.loaders.filesystem.Loader',
+                'django.template.loaders.app_directories.Loader',
+            ]),
+        ],
+    },
+}])
 class PriorityCacheLoader(SimpleTestCase):
     def test_basic(self):
         """
@@ -229,10 +237,16 @@ class PriorityCacheLoader(SimpleTestCase):
         self.assertEqual(t1.render(Context({})), 'priority\n')
 
 
-@override_settings(
-    TEMPLATE_LOADERS=('django.template.loaders.filesystem.Loader',
-                      'django.template.loaders.app_directories.Loader',),
-)
+@override_settings(TEMPLATES=[{
+    'BACKEND': 'django.template.backends.django.DjangoTemplates',
+    'DIRS': [GLOBAL_TEMPLATES_DIR],
+    'OPTIONS': {
+        'loaders': [
+            'django.template.loaders.filesystem.Loader',
+            'django.template.loaders.app_directories.Loader',
+        ],
+    },
+}])
 class PriorityLoader(SimpleTestCase):
     def test_basic(self):
         """

+ 65 - 34
tests/template_tests/tests.py

@@ -17,6 +17,11 @@ from django.test.utils import override_settings, extend_sys_path
 from django.utils._os import upath
 
 
+TESTS_DIR = os.path.dirname(os.path.dirname(os.path.abspath(upath(__file__))))
+
+TEMPLATES_DIR = os.path.join(TESTS_DIR, 'templates')
+
+
 class TemplateLoaderTests(SimpleTestCase):
 
     def test_loaders_security(self):
@@ -73,7 +78,10 @@ class TemplateLoaderTests(SimpleTestCase):
             test_template_sources('/DIR1/index.HTML', template_dirs,
                                   ['/DIR1/index.HTML'])
 
-    @override_settings(TEMPLATE_LOADERS=['django.template.loaders.filesystem.Loader'])
+    @override_settings(TEMPLATES=[{
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [TEMPLATES_DIR],
+    }])
     # Turn TEMPLATE_DEBUG on, so that the origin file name will be kept with
     # the compiled templates.
     @override_settings(TEMPLATE_DEBUG=True)
@@ -90,10 +98,17 @@ class TemplateLoaderTests(SimpleTestCase):
         self.assertTrue(template_name.endswith(load_name),
             'Template loaded by filesystem loader has incorrect name for debug page: %s' % template_name)
 
-    @override_settings(TEMPLATE_LOADERS=[
-        ('django.template.loaders.cached.Loader',
-            ['django.template.loaders.filesystem.Loader']),
-    ])
+    @override_settings(TEMPLATES=[{
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'DIRS': [TEMPLATES_DIR],
+        'OPTIONS': {
+            'loaders': [
+                ('django.template.loaders.cached.Loader', [
+                    'django.template.loaders.filesystem.Loader',
+                ]),
+            ],
+        },
+    }])
     @override_settings(TEMPLATE_DEBUG=True)
     def test_cached_loader_debug_origin(self):
         # Same comment as in test_loader_debug_origin.
@@ -130,7 +145,10 @@ class TemplateLoaderTests(SimpleTestCase):
     # Test the base loader class via the app loader. load_template
     # from base is used by all shipped loaders excepting cached,
     # which has its own test.
-    @override_settings(TEMPLATE_LOADERS=['django.template.loaders.app_directories.Loader'])
+    @override_settings(TEMPLATES=[{
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'APP_DIRS': True,
+    }])
     def test_include_missing_template(self):
         """
         Tests that the correct template is identified as not existing
@@ -151,7 +169,10 @@ class TemplateLoaderTests(SimpleTestCase):
     # Test the base loader class via the app loader. load_template
     # from base is used by all shipped loaders excepting cached,
     # which has its own test.
-    @override_settings(TEMPLATE_LOADERS=['django.template.loaders.app_directories.Loader'])
+    @override_settings(TEMPLATES=[{
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'APP_DIRS': True,
+    }])
     def test_extends_include_missing_baseloader(self):
         """
         Tests that the correct template is identified as not existing
@@ -168,34 +189,39 @@ class TemplateLoaderTests(SimpleTestCase):
             self.assertEqual(e.args[0], 'missing.html')
         self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
 
+    @override_settings(TEMPLATES=[{
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'OPTIONS': {
+            'loaders': [
+                ('django.template.loaders.cached.Loader', [
+                    'django.template.loaders.app_directories.Loader',
+                ]),
+            ],
+        },
+    }])
     @override_settings(TEMPLATE_DEBUG=True)
     def test_extends_include_missing_cachedloader(self):
         """
         Same as test_extends_include_missing_baseloader, only tests
         behavior of the cached loader instead of base loader.
         """
-        with override_settings(TEMPLATE_LOADERS=[
-            ('django.template.loaders.cached.Loader', [
-                'django.template.loaders.app_directories.Loader',
-            ]),
-        ]):
-            load_name = 'test_extends_error.html'
-            tmpl = loader.get_template(load_name)
-            r = None
-            try:
-                r = tmpl.render(template.Context({}))
-            except template.TemplateDoesNotExist as e:
-                self.assertEqual(e.args[0], 'missing.html')
-            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
-
-            # For the cached loader, repeat the test, to ensure the first attempt did not cache a
-            # result that behaves incorrectly on subsequent attempts.
-            tmpl = loader.get_template(load_name)
-            try:
-                tmpl.render(template.Context({}))
-            except template.TemplateDoesNotExist as e:
-                self.assertEqual(e.args[0], 'missing.html')
-            self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
+        load_name = 'test_extends_error.html'
+        tmpl = loader.get_template(load_name)
+        r = None
+        try:
+            r = tmpl.render(template.Context({}))
+        except template.TemplateDoesNotExist as e:
+            self.assertEqual(e.args[0], 'missing.html')
+        self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
+
+        # For the cached loader, repeat the test, to ensure the first attempt did not cache a
+        # result that behaves incorrectly on subsequent attempts.
+        tmpl = loader.get_template(load_name)
+        try:
+            tmpl.render(template.Context({}))
+        except template.TemplateDoesNotExist as e:
+            self.assertEqual(e.args[0], 'missing.html')
+        self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
 
     def test_include_template_argument(self):
         """
@@ -429,11 +455,16 @@ class RequestContextTests(unittest.TestCase):
     def setUp(self):
         self.fake_request = RequestFactory().get('/')
 
-    @override_settings(TEMPLATE_LOADERS=[
-        ('django.template.loaders.locmem.Loader', {
-            'child': '{{ var|default:"none" }}',
-        }),
-    ])
+    @override_settings(TEMPLATES=[{
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'OPTIONS': {
+            'loaders': [
+                ('django.template.loaders.locmem.Loader', {
+                    'child': '{{ var|default:"none" }}',
+                }),
+            ],
+        },
+    }])
     def test_include_only(self):
         """
         Regression test for #15721, ``{% include %}`` and ``RequestContext``

+ 20 - 13
tests/view_tests/tests/test_debug.py

@@ -63,21 +63,28 @@ class DebugViewTests(TestCase):
         response = self.client.get('/raises400/')
         self.assertContains(response, '<div class="context" id="', status_code=400)
 
+    # Ensure no 403.html template exists to test the default case.
+    @override_settings(TEMPLATES=[{
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+    }])
     def test_403(self):
-        # Ensure no 403.html template exists to test the default case.
-        with override_settings(TEMPLATE_LOADERS=[]):
-            response = self.client.get('/raises403/')
-            self.assertContains(response, '<h1>403 Forbidden</h1>', status_code=403)
-
+        response = self.client.get('/raises403/')
+        self.assertContains(response, '<h1>403 Forbidden</h1>', status_code=403)
+
+    # Set up a test 403.html template.
+    @override_settings(TEMPLATES=[{
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'OPTIONS': {
+            'loaders': [
+                ('django.template.loaders.locmem.Loader', {
+                    '403.html': 'This is a test template for a 403 error.',
+                }),
+            ],
+        },
+    }])
     def test_403_template(self):
-        # Set up a test 403.html template.
-        with override_settings(TEMPLATE_LOADERS=[
-            ('django.template.loaders.locmem.Loader', {
-                '403.html': 'This is a test template for a 403 Forbidden error.',
-            })
-        ]):
-            response = self.client.get('/raises403/')
-            self.assertContains(response, 'test template', status_code=403)
+        response = self.client.get('/raises403/')
+        self.assertContains(response, 'test template', status_code=403)
 
     def test_404(self):
         response = self.client.get('/raises404/')

+ 15 - 10
tests/view_tests/tests/test_defaults.py

@@ -35,21 +35,26 @@ class DefaultsTests(TestCase):
         response = self.client.get('/server_error/')
         self.assertEqual(response.status_code, 500)
 
+    @override_settings(TEMPLATES=[{
+        'BACKEND': 'django.template.backends.django.DjangoTemplates',
+        'OPTIONS': {
+            'loaders': [
+                ('django.template.loaders.locmem.Loader', {
+                    '404.html': 'This is a test template for a 404 error.',
+                    '500.html': 'This is a test template for a 500 error.',
+                }),
+            ],
+        },
+    }])
     def test_custom_templates(self):
         """
         Test that 404.html and 500.html templates are picked by their respective
         handler.
         """
-        with override_settings(TEMPLATE_LOADERS=[
-            ('django.template.loaders.locmem.Loader', {
-                '404.html': 'This is a test template for a 404 error.',
-                '500.html': 'This is a test template for a 500 error.',
-            }),
-        ]):
-            for code, url in ((404, '/non_existing_url/'), (500, '/server_error/')):
-                response = self.client.get(url)
-                self.assertContains(response, "test template for a %d error" % code,
-                    status_code=code)
+        for code, url in ((404, '/non_existing_url/'), (500, '/server_error/')):
+            response = self.client.get(url)
+            self.assertContains(response, "test template for a %d error" % code,
+                status_code=code)
 
     def test_get_absolute_url_attributes(self):
         "A model can set attributes on the get_absolute_url method"