Fixed #32460 -- Allowed "label"/"do_not_call_in_templates" members in model choice enums.

This commit is contained in:
Nick Pope 2021-02-19 17:17:11 +00:00 committed by Mariusz Felisiak
parent 41e39c41c9
commit a96c730431
2 changed files with 31 additions and 6 deletions

View File

@ -1,4 +1,5 @@
import enum
from types import DynamicClassAttribute
from django.utils.functional import Promise
@ -26,12 +27,8 @@ class ChoicesMeta(enum.EnumMeta):
# assignment in enum's classdict.
dict.__setitem__(classdict, key, value)
cls = super().__new__(metacls, classname, bases, classdict, **kwds)
cls._value2label_map_ = dict(zip(cls._value2member_map_, labels))
# Add a label property to instances of enum which uses the enum member
# that is passed in as "self" as the value to use when looking up the
# label in the choices.
cls.label = property(lambda self: cls._value2label_map_.get(self.value))
cls.do_not_call_in_templates = True
for member, label in zip(cls.__members__.values(), labels):
member._label_ = label
return enum.unique(cls)
def __contains__(cls, member):
@ -62,6 +59,14 @@ class ChoicesMeta(enum.EnumMeta):
class Choices(enum.Enum, metaclass=ChoicesMeta):
"""Class for creating enumerated choices."""
@DynamicClassAttribute
def label(self):
return self._label_
@property
def do_not_call_in_templates(self):
return True
def __str__(self):
"""
Use value when cast to str, so that Choices set as model instance

View File

@ -159,6 +159,26 @@ class ChoicesTests(SimpleTestCase):
with self.assertRaises(AttributeError):
models.TextChoices('Properties', 'choices labels names values')
def test_label_member(self):
# label can be used as a member.
Stationery = models.TextChoices('Stationery', 'label stamp sticker')
self.assertEqual(Stationery.label.label, 'Label')
self.assertEqual(Stationery.label.value, 'label')
self.assertEqual(Stationery.label.name, 'label')
def test_do_not_call_in_templates_member(self):
# do_not_call_in_templates is not implicitly treated as a member.
Special = models.IntegerChoices('Special', 'do_not_call_in_templates')
self.assertEqual(
Special.do_not_call_in_templates.label,
'Do Not Call In Templates',
)
self.assertEqual(Special.do_not_call_in_templates.value, 1)
self.assertEqual(
Special.do_not_call_in_templates.name,
'do_not_call_in_templates',
)
class Separator(bytes, models.Choices):
FS = b'\x1c', 'File Separator'