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:
@@ -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."""
|
||||
|
||||
@@ -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"):
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user