mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Fixed #35735 -- Enabled template access to methods and properties of classes with __class_get_item__.
This commit is contained in:
parent
8b9a2bf34e
commit
d2c97981fb
@ -880,6 +880,10 @@ class Variable:
|
|||||||
try: # catch-all for silent variable failures
|
try: # catch-all for silent variable failures
|
||||||
for bit in self.lookups:
|
for bit in self.lookups:
|
||||||
try: # dictionary lookup
|
try: # dictionary lookup
|
||||||
|
# Only allow if the metaclass implements __getitem__. See
|
||||||
|
# https://docs.python.org/3/reference/datamodel.html#classgetitem-versus-getitem
|
||||||
|
if not hasattr(type(current), "__getitem__"):
|
||||||
|
raise TypeError
|
||||||
current = current[bit]
|
current = current[bit]
|
||||||
# ValueError/IndexError are for numpy.array lookup on
|
# ValueError/IndexError are for numpy.array lookup on
|
||||||
# numpy < 1.9 and 1.9+ respectively
|
# numpy < 1.9 and 1.9+ respectively
|
||||||
|
@ -346,6 +346,52 @@ class BasicSyntaxTests(SimpleTestCase):
|
|||||||
output = self.engine.render_to_string("tpl-weird-percent")
|
output = self.engine.render_to_string("tpl-weird-percent")
|
||||||
self.assertEqual(output, "% %s")
|
self.assertEqual(output, "% %s")
|
||||||
|
|
||||||
|
@setup(
|
||||||
|
{"template": "{{ class_var.class_property }} | {{ class_var.class_method }}"}
|
||||||
|
)
|
||||||
|
def test_subscriptable_class(self):
|
||||||
|
class MyClass(list):
|
||||||
|
# As of Python 3.9 list defines __class_getitem__ which makes it
|
||||||
|
# subscriptable.
|
||||||
|
class_property = "Example property"
|
||||||
|
do_not_call_in_templates = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def class_method(cls):
|
||||||
|
return "Example method"
|
||||||
|
|
||||||
|
for case in (MyClass, lambda: MyClass):
|
||||||
|
with self.subTest(case=case):
|
||||||
|
output = self.engine.render_to_string("template", {"class_var": case})
|
||||||
|
self.assertEqual(output, "Example property | Example method")
|
||||||
|
|
||||||
|
@setup({"template": "{{ meals.lunch }}"})
|
||||||
|
def test_access_class_property_if_getitem_is_defined_in_metaclass(self):
|
||||||
|
"""
|
||||||
|
If the metaclass defines __getitem__, the template system should use
|
||||||
|
it to resolve the dot notation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class MealMeta(type):
|
||||||
|
def __getitem__(cls, name):
|
||||||
|
return getattr(cls, name) + " is yummy."
|
||||||
|
|
||||||
|
class Meals(metaclass=MealMeta):
|
||||||
|
lunch = "soup"
|
||||||
|
do_not_call_in_templates = True
|
||||||
|
|
||||||
|
# Make class type subscriptable.
|
||||||
|
def __class_getitem__(cls, key):
|
||||||
|
from types import GenericAlias
|
||||||
|
|
||||||
|
return GenericAlias(cls, key)
|
||||||
|
|
||||||
|
self.assertEqual(Meals.lunch, "soup")
|
||||||
|
self.assertEqual(Meals["lunch"], "soup is yummy.")
|
||||||
|
|
||||||
|
output = self.engine.render_to_string("template", {"meals": Meals})
|
||||||
|
self.assertEqual(output, "soup is yummy.")
|
||||||
|
|
||||||
|
|
||||||
class BlockContextTests(SimpleTestCase):
|
class BlockContextTests(SimpleTestCase):
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user