An attempt to convert ChoiceField into TypedChoiceField

This commit is contained in:
Kirill Gagarski 2019-12-14 00:35:21 +03:00
parent de497a9bf1
commit 00b7bb8110

View File

@ -1432,28 +1432,44 @@ class ChoiceField(Field):
html_cutoff = None html_cutoff = None
html_cutoff_text = _('More than {count} items...') html_cutoff_text = _('More than {count} items...')
def __init__(self, choices, **kwargs): def __init__(self, choices, underlying_field=None, **kwargs):
self.underlying_field = underlying_field
self.choices = choices self.choices = choices
self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff) self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff)
self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text) self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text)
self.allow_blank = kwargs.pop('allow_blank', False) self.allow_blank = kwargs.pop('allow_blank', False)
super().__init__(**kwargs) super().__init__(**kwargs)
@staticmethod
def _hashable_wrapper(value):
if isinstance(value, list):
return tuple(elem for elem in value)
elif isinstance(value, set):
return frozenset(value)
elif isinstance(value, dict):
return frozenset(value.items())
else:
return value
def _choices_key(self, value):
representation = self.underlying_field.to_representation(value) if self.underlying_field else str(value)
return self._hashable_wrapper(representation)
def to_internal_value(self, data): def to_internal_value(self, data):
if data == '' and self.allow_blank: if data == '' and self.allow_blank:
return '' return ''
try: try:
return self.choice_strings_to_values[str(data)] return self.choice_reprs_to_values[self._choices_key(data)]
except KeyError: except (KeyError, TypeError) as e:
self.fail('invalid_choice', input=data) self.fail('invalid_choice', input=data)
def to_representation(self, value): def to_representation(self, value):
if value in ('', None): # Preserving old untyped behavior
if not self.underlying_field:
return value return value
return self.choice_strings_to_values.get(str(value), value) return self.underlying_field.to_representation(value)
def iter_options(self): def iter_options(self):
""" """
@ -1472,11 +1488,11 @@ class ChoiceField(Field):
self.grouped_choices = to_choices_dict(choices) self.grouped_choices = to_choices_dict(choices)
self._choices = flatten_choices_dict(self.grouped_choices) self._choices = flatten_choices_dict(self.grouped_choices)
# Map the string representation of choices to the underlying value. # Map the representation (evaluated with underlying_field) of choices to the underlying value.
# Allows us to deal with eg. integer choices while supporting either # Allows us to deal with eg. integer choices while supporting either
# integer or string input, but still get the correct datatype out. # integer or string input, but still get the correct datatype out.
self.choice_strings_to_values = { self.choice_reprs_to_values = {
str(key): key for key in self.choices self._choices_key(key): key for key in self.choices
} }
choices = property(_get_choices, _set_choices) choices = property(_get_choices, _set_choices)
@ -1517,7 +1533,7 @@ class MultipleChoiceField(ChoiceField):
def to_representation(self, value): def to_representation(self, value):
return { return {
self.choice_strings_to_values.get(str(item), item) for item in value self.underlying_field.to_representation(item) if self.underlying_field else value for item in value
} }