mirror of
				https://github.com/django/django.git
				synced 2025-10-30 17:16:10 +00:00 
			
		
		
		
	Fixed #35735 -- Enabled template access to methods and properties of classes with __class_get_item__.
This commit is contained in:
		
				
					committed by
					
						 Sarah Boyce
						Sarah Boyce
					
				
			
			
				
	
			
			
			
						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): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user