Skip to content

Commit

Permalink
Fixed #27359 -- Made Engine.get_default() return the first DjangoTemp…
Browse files Browse the repository at this point in the history
…lates engine if multiple are defined.
  • Loading branch information
Carlton Gibson authored and timgraham committed Mar 31, 2017
1 parent 7019724 commit 6b3724f
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 25 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ answer newbie questions, and generally made Django that much better:
Carl Meyer <[email protected]>
Carlos Eduardo de Paula <[email protected]>
Carlos Matías de la Torre <[email protected]>
Carlton Gibson <[email protected]>
[email protected]
ChaosKCW
Charlie Leifer <[email protected]>
Expand Down
21 changes: 6 additions & 15 deletions django/template/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ def __init__(self, dirs=None, app_dirs=False, context_processors=None,
@functools.lru_cache()
def get_default():
"""
When only one DjangoTemplates backend is configured, return it.
Raise ImproperlyConfigured otherwise.
Return the first DjangoTemplates backend that's configured, or raise
ImproperlyConfigured if none are configured.
This is required for preserving historical APIs that rely on a
globally available, implicitly configured engine such as:
Expand All @@ -74,18 +73,10 @@ def get_default():
# local imports are required to avoid import loops.
from django.template import engines
from django.template.backends.django import DjangoTemplates
django_engines = [engine for engine in engines.all()
if isinstance(engine, DjangoTemplates)]
if len(django_engines) == 1:
# Unwrap the Engine instance inside DjangoTemplates
return django_engines[0].engine
elif len(django_engines) == 0:
raise ImproperlyConfigured(
"No DjangoTemplates backend is configured.")
else:
raise ImproperlyConfigured(
"Several DjangoTemplates backends are configured. "
"You must select one explicitly.")
for engine in engines.all():
if isinstance(engine, DjangoTemplates):
return engine.engine
raise ImproperlyConfigured('No DjangoTemplates backend is configured.')

@cached_property
def template_context_processors(self):
Expand Down
20 changes: 14 additions & 6 deletions docs/ref/templates/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,20 @@ what's passed by :class:`~django.template.backends.django.DjangoTemplates`.

.. staticmethod:: Engine.get_default()

When a Django project configures one and only one
:class:`~django.template.backends.django.DjangoTemplates` engine, this
method returns the underlying :class:`Engine`. In other circumstances it
will raise :exc:`~django.core.exceptions.ImproperlyConfigured`.
Returns the underlying :class:`Engine` from the first configured
:class:`~django.template.backends.django.DjangoTemplates` engine. Raises
:exc:`~django.core.exceptions.ImproperlyConfigured` if no engines are
configured.

It's required for preserving APIs that rely on a globally available,
implicitly configured engine. Any other use is strongly discouraged.

.. versionchanged:: 2.0

In older versions, raises
:exc:`~django.core.exceptions.ImproperlyConfigured` if multiple
engines are configured rather than returning the first engine.

.. method:: Engine.from_string(template_code)

Compiles the given template code and returns a :class:`Template` object.
Expand All @@ -175,9 +181,11 @@ The recommended way to create a :class:`Template` is by calling the factory
methods of the :class:`Engine`: :meth:`~Engine.get_template`,
:meth:`~Engine.select_template` and :meth:`~Engine.from_string`.

In a Django project where the :setting:`TEMPLATES` setting defines exactly one
In a Django project where the :setting:`TEMPLATES` setting defines a
:class:`~django.template.backends.django.DjangoTemplates` engine, it's
possible to instantiate a :class:`Template` directly.
possible to instantiate a :class:`Template` directly. If more than one
:class:`~django.template.backends.django.DjangoTemplates` engine is defined,
the first one will be used.

.. class:: Template

Expand Down
4 changes: 3 additions & 1 deletion docs/releases/2.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ Signals
Templates
~~~~~~~~~

* ...
* To increase the usefulness of :meth:`.Engine.get_default` in third-party
apps, it now returns the first engine if multiple ``DjangoTemplates`` engines
are configured in ``TEMPLATES`` rather than raising ``ImproperlyConfigured``.

Tests
~~~~~
Expand Down
6 changes: 3 additions & 3 deletions tests/template_tests/test_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ def test_single_engine_configured(self):
@override_settings(TEMPLATES=[{
'NAME': 'default',
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {'file_charset': 'abc'},
}, {
'NAME': 'other',
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'OPTIONS': {'file_charset': 'def'},
}])
def test_multiple_engines_configured(self):
msg = 'Several DjangoTemplates backends are configured. You must select one explicitly.'
with self.assertRaisesMessage(ImproperlyConfigured, msg):
Engine.get_default()
self.assertEqual(Engine.get_default().file_charset, 'abc')


class LoaderTests(SimpleTestCase):
Expand Down

0 comments on commit 6b3724f

Please sign in to comment.