diff options
| author | Nick Pope <nick@nickpope.me.uk> | 2023-08-31 02:57:40 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-08-30 22:57:40 -0300 |
| commit | 500e01073adda32d5149624ee9a5cb7aa3d3583f (patch) | |
| tree | f9416872a811aa39646deaf002414e0a7841b6d1 /django/forms | |
| parent | 68a8996bdfce2d191decd7b1c1a2b9fdea8e4b2f (diff) | |
Fixed #31262 -- Added support for mappings on model fields and ChoiceField's choices.
Diffstat (limited to 'django/forms')
| -rw-r--r-- | django/forms/fields.py | 32 | ||||
| -rw-r--r-- | django/forms/models.py | 5 | ||||
| -rw-r--r-- | django/forms/widgets.py | 14 |
3 files changed, 21 insertions, 30 deletions
diff --git a/django/forms/fields.py b/django/forms/fields.py index bd226de543..3a0510ffc9 100644 --- a/django/forms/fields.py +++ b/django/forms/fields.py @@ -17,7 +17,6 @@ from urllib.parse import urlsplit, urlunsplit from django.core import validators from django.core.exceptions import ValidationError -from django.db.models.enums import ChoicesMeta from django.forms.boundfield import BoundField from django.forms.utils import from_current_timezone, to_current_timezone from django.forms.widgets import ( @@ -42,6 +41,7 @@ from django.forms.widgets import ( URLInput, ) from django.utils import formats +from django.utils.choices import normalize_choices from django.utils.dateparse import parse_datetime, parse_duration from django.utils.deprecation import RemovedInDjango60Warning from django.utils.duration import duration_string @@ -861,14 +861,6 @@ class NullBooleanField(BooleanField): pass -class CallableChoiceIterator: - def __init__(self, choices_func): - self.choices_func = choices_func - - def __iter__(self): - yield from self.choices_func() - - class ChoiceField(Field): widget = Select default_error_messages = { @@ -879,8 +871,6 @@ class ChoiceField(Field): def __init__(self, *, choices=(), **kwargs): super().__init__(**kwargs) - if isinstance(choices, ChoicesMeta): - choices = choices.choices self.choices = choices def __deepcopy__(self, memo): @@ -888,21 +878,15 @@ class ChoiceField(Field): result._choices = copy.deepcopy(self._choices, memo) return result - def _get_choices(self): + @property + def choices(self): return self._choices - def _set_choices(self, value): - # Setting choices also sets the choices on the widget. - # choices can be any iterable, but we call list() on it because - # it will be consumed more than once. - if callable(value): - value = CallableChoiceIterator(value) - else: - value = list(value) - - self._choices = self.widget.choices = value - - choices = property(_get_choices, _set_choices) + @choices.setter + def choices(self, value): + # Setting choices on the field also sets the choices on the widget. + # Note that the property setter for the widget will re-normalize. + self._choices = self.widget.choices = normalize_choices(value) def to_python(self, value): """Return a string.""" diff --git a/django/forms/models.py b/django/forms/models.py index dc30d79b5d..d353da4ddc 100644 --- a/django/forms/models.py +++ b/django/forms/models.py @@ -21,6 +21,7 @@ from django.forms.widgets import ( RadioSelect, SelectMultiple, ) +from django.utils.choices import ChoiceIterator from django.utils.text import capfirst, get_text_list from django.utils.translation import gettext from django.utils.translation import gettext_lazy as _ @@ -1402,7 +1403,7 @@ class ModelChoiceIteratorValue: return self.value == other -class ModelChoiceIterator: +class ModelChoiceIterator(ChoiceIterator): def __init__(self, field): self.field = field self.queryset = field.queryset @@ -1532,7 +1533,7 @@ class ModelChoiceField(ChoiceField): # the queryset. return self.iterator(self) - choices = property(_get_choices, ChoiceField._set_choices) + choices = property(_get_choices, ChoiceField.choices.fset) def prepare_value(self, value): if hasattr(value, "_meta"): diff --git a/django/forms/widgets.py b/django/forms/widgets.py index ab7c0f755f..2c734052d5 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -12,6 +12,7 @@ from itertools import chain from django.forms.utils import to_current_timezone from django.templatetags.static import static from django.utils import formats +from django.utils.choices import normalize_choices from django.utils.dates import MONTHS from django.utils.formats import get_format from django.utils.html import format_html, html_safe @@ -620,10 +621,7 @@ class ChoiceWidget(Widget): def __init__(self, attrs=None, choices=()): super().__init__(attrs) - # choices can be any iterable, but we may need to render this widget - # multiple times. Thus, collapse it into a list so it can be consumed - # more than once. - self.choices = list(choices) + self.choices = choices def __deepcopy__(self, memo): obj = copy.copy(self) @@ -741,6 +739,14 @@ class ChoiceWidget(Widget): value = [value] return [str(v) if v is not None else "" for v in value] + @property + def choices(self): + return self._choices + + @choices.setter + def choices(self, value): + self._choices = normalize_choices(value) + class Select(ChoiceWidget): input_type = "select" |
