Ticket #17668: 17668.diff

File 17668.diff, 3.6 KB (added by akaariai, 4 years ago)
  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    index 41c24c7..6ae0a52 100644
    a b class QuerySet(object): 
    8282            if self._iter:
    8383                self._result_cache = list(self._iter)
    8484            else:
    85                 self._result_cache = list(self.iterator())
     85                self._result_cache = list(self.iterator(_check_prefetch=False))
    8686        elif self._iter:
    8787            self._result_cache.extend(self._iter)
    8888        if self._prefetch_related_lookups and not self._prefetch_done:
    class QuerySet(object): 
    228228    # METHODS THAT DO DATABASE QUERIES #
    229229    ####################################
    230230
    231     def iterator(self):
     231    def iterator(self, _check_prefetch=True):
    232232        """
    233233        An iterator over the results from applying this QuerySet to the
    234234        database.
     235
     236        Calling this method with _check_prefetch=True will cause an error
     237        if there are prefetches defined for the qs. Internal methods can
     238        skip prefetch_related checks by defining _check_prefetch=False.
    235239        """
     240        if _check_prefetch and self._prefetch_related_lookups:
     241            raise ValueError("Using iterator() when prefetch_related is used "
     242                             "is not supported")
    236243        fill_cache = False
    237244        if connections[self.db].features.supports_select_related:
    238245            fill_cache = self.query.select_related
    class QuerySet(object): 
    485492        qs = self._clone()
    486493        qs.query.add_filter(('pk__in', id_list))
    487494        qs.query.clear_ordering(force_empty=True)
    488         return dict([(obj._get_pk_val(), obj) for obj in qs.iterator()])
     495        # Populate the query, making sure prefetch_related etc. are handled
     496        # properly.
     497        objs = list(self)
     498        return dict([(obj._get_pk_val(), obj) for obj in objs])
    489499
    490500    def delete(self):
    491501        """
  • docs/ref/models/querysets.txt

    diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
    index 103cae1..725f38d 100644
    a b performance and a significant reduction in memory. 
    14301430Note that using ``iterator()`` on a ``QuerySet`` which has already been
    14311431evaluated will force it to evaluate again, repeating the query.
    14321432
     1433It is an error to use ``iterator()`` when ``prefetch_related`` is also used
     1434in the same ``QuerySet``.
     1435
    14331436latest
    14341437~~~~~~
    14351438
  • tests/modeltests/prefetch_related/tests.py

    diff --git a/tests/modeltests/prefetch_related/tests.py b/tests/modeltests/prefetch_related/tests.py
    index 4c51a83..f457fc9 100644
    a b class NullableTest(TestCase): 
    470470                        for e in qs2]
    471471
    472472        self.assertEqual(co_serfs, co_serfs2)
     473
     474class TestIterator(TestCase):
     475
     476    def test_iterator_raises_error(self):
     477        """
     478        Using an iterator when prefetch_related is defined is an error.
     479        """
     480        def raises():
     481            list(Employee.objects.prefetch_related('boss').iterator())
     482        self.assertRaises(ValueError, raises)
     483
     484    def test_in_bulk(self):
     485        """
     486        In-bulk does correctly prefetch objects by not using .iterator()
     487        directly.
     488        """
     489        boss = Employee.objects.create(name="Peter")
     490        boss = Employee.objects.create(name="Jack")
     491        with self.assertNumQueries(2):
     492            # Check that prefetch is done and it does not cause any errors.
     493            bulk = Employee.objects.prefetch_related('serfs').in_bulk([boss.pk])
     494            for b in bulk.values():
     495                list(b.serfs.all())
Back to Top