summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorNick Pope <nick@nickpope.me.uk>2023-10-16 19:11:18 +0100
committerNatalia <124304+nessita@users.noreply.github.com>2023-10-23 14:54:37 -0300
commitcc5901fa8edc25ce6d67d110c18ddf9f16965e32 (patch)
treed0af62584c37f39f20caf04fab8193c1823a0d25 /django
parentbbe90f3c00eb29a8e86b1b638466029def7f444a (diff)
[5.0.x] Fixed #34899 -- Added blank choice to forms' callable choices lazily.
Backport of 171f91d9ef5177850c2f12b26dd732785f6ac034 from main
Diffstat (limited to 'django')
-rw-r--r--django/db/models/fields/__init__.py10
-rw-r--r--django/utils/choices.py17
2 files changed, 19 insertions, 8 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 6174b7bc98..205a41c193 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -16,6 +16,7 @@ from django.db.models.constants import LOOKUP_SEP
from django.db.models.query_utils import DeferredAttribute, RegisterLookupMixin
from django.utils import timezone
from django.utils.choices import (
+ BlankChoiceIterator,
CallableChoiceIterator,
flatten_choices,
normalize_choices,
@@ -1055,14 +1056,9 @@ class Field(RegisterLookupMixin):
as <select> choices for this field.
"""
if self.choices is not None:
- choices = list(self.choices)
if include_blank:
- blank_defined = any(
- choice in ("", None) for choice, _ in self.flatchoices
- )
- if not blank_defined:
- choices = blank_choice + choices
- return choices
+ return BlankChoiceIterator(self.choices, blank_choice)
+ return self.choices
rel_model = self.remote_field.model
limit_choices_to = limit_choices_to or self.get_limit_choices_to()
choice_func = operator.attrgetter(
diff --git a/django/utils/choices.py b/django/utils/choices.py
index 54dbdcb3aa..7f40bce510 100644
--- a/django/utils/choices.py
+++ b/django/utils/choices.py
@@ -1,10 +1,11 @@
from collections.abc import Callable, Iterable, Iterator, Mapping
-from itertools import islice, zip_longest
+from itertools import islice, tee, zip_longest
from django.utils.functional import Promise
__all__ = [
"BaseChoiceIterator",
+ "BlankChoiceIterator",
"CallableChoiceIterator",
"flatten_choices",
"normalize_choices",
@@ -34,6 +35,20 @@ class BaseChoiceIterator:
)
+class BlankChoiceIterator(BaseChoiceIterator):
+ """Iterator to lazily inject a blank choice."""
+
+ def __init__(self, choices, blank_choice):
+ self.choices = choices
+ self.blank_choice = blank_choice
+
+ def __iter__(self):
+ choices, other = tee(self.choices)
+ if not any(value in ("", None) for value, _ in flatten_choices(other)):
+ yield from self.blank_choice
+ yield from choices
+
+
class CallableChoiceIterator(BaseChoiceIterator):
"""Iterator to lazily normalize choices generated by a callable."""