1
0
mirror of https://github.com/django/django.git synced 2025-10-31 09:41:08 +00:00

Fixed #31262 -- Added support for mappings on model fields and ChoiceField's choices.

This commit is contained in:
Nick Pope
2023-08-31 02:57:40 +01:00
committed by GitHub
parent 68a8996bdf
commit 500e01073a
29 changed files with 822 additions and 249 deletions

View File

@@ -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."""

View File

@@ -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"):

View File

@@ -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"