summaryrefslogtreecommitdiff
path: root/django/forms
diff options
context:
space:
mode:
Diffstat (limited to 'django/forms')
-rw-r--r--django/forms/models.py26
-rw-r--r--django/forms/widgets.py6
2 files changed, 24 insertions, 8 deletions
diff --git a/django/forms/models.py b/django/forms/models.py
index 9686baa6f2..c04a806caa 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -3,6 +3,7 @@ Helper functions for creating Form classes from Django models
and database field objects.
"""
+from collections import deque
from itertools import chain
from django.core.exceptions import (
@@ -1438,17 +1439,20 @@ class ModelChoiceIterator(BaseChoiceIterator):
def __init__(self, field):
self.field = field
self.queryset = field.queryset
+ self._deque = deque()
+ self._generator = self.generator()
- def __iter__(self):
+ def generator(self):
if self.field.empty_label is not None:
yield ("", self.field.empty_label)
- queryset = self.queryset
- # Can't use iterator() when queryset uses prefetch_related()
- if not queryset._prefetch_related_lookups:
- queryset = queryset.iterator()
- for obj in queryset:
+ for obj in self.queryset.iterator(chunk_size=100):
yield self.choice(obj)
+ def __iter__(self):
+ yield from chain(self._deque, self._generator)
+ self._deque = deque()
+ self._generator = self.generator()
+
def __len__(self):
# count() adds a query but uses less memory since the QuerySet results
# won't be cached. In most cases, the choices will only be iterated on,
@@ -1456,7 +1460,9 @@ class ModelChoiceIterator(BaseChoiceIterator):
return self.queryset.count() + (1 if self.field.empty_label is not None else 0)
def __bool__(self):
- return self.field.empty_label is not None or self.queryset.exists()
+ return (
+ self.field.empty_label is not None or self._deque or self.queryset.exists()
+ )
def choice(self, obj):
return (
@@ -1464,6 +1470,12 @@ class ModelChoiceIterator(BaseChoiceIterator):
self.field.label_from_instance(obj),
)
+ def peek(self):
+ value = next(self._generator, None)
+ if value is not None:
+ self._deque.append(value)
+ return value
+
class ModelChoiceField(ChoiceField):
"""A ChoiceField whose choices are a model QuerySet."""
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 1bcfeba288..82685a3fea 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -851,7 +851,11 @@ class Select(ChoiceWidget):
if self.allow_multiple_selected:
return use_required_attribute
- first_choice = next(iter(self.choices), None)
+ first_choice = (
+ self.choices.peek()
+ if hasattr(self.choices, "peek")
+ else next(iter(self.choices), None)
+ )
return (
use_required_attribute
and first_choice is not None