diff --git a/django/db/models/query.py b/django/db/models/query.py index 53ee999d0d..c54380b96f 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -1545,8 +1545,18 @@ def prefetch_related_objects(result_cache, related_lookups): if len(obj_list) == 0: break + current_lookup = LOOKUP_SEP.join(attrs[0:level+1]) + if current_lookup in done_queries: + # Skip any prefetching, and any object preparation + obj_list = done_queries[current_lookup] + continue + + # Prepare objects: good_objects = True for obj in obj_list: + # Since prefetching can re-use instances, it is possible to have + # the same instance multiple times in obj_list, so obj might + # already be prepared. if not hasattr(obj, '_prefetched_objects_cache'): try: obj._prefetched_objects_cache = {} @@ -1557,14 +1567,6 @@ def prefetch_related_objects(result_cache, related_lookups): # now. good_objects = False break - else: - # Since prefetching can re-use instances, it is possible to - # have the same instance multiple times in obj_list. So we - # can reach this branch either because we did all of - # obj_list already, or because we did 'obj' earlier in this - # iteration over obj_list. In the first case we could - # shortcut and exit the loop, but not in the second. - continue if not good_objects: break @@ -1589,23 +1591,18 @@ def prefetch_related_objects(result_cache, related_lookups): "prefetch_related()." % lookup) if prefetcher is not None and not is_fetched: - # Check we didn't do this already - current_lookup = LOOKUP_SEP.join(attrs[0:level+1]) - if current_lookup in done_queries: - obj_list = done_queries[current_lookup] - else: - obj_list, additional_prl = prefetch_one_level(obj_list, prefetcher, attr) - # We need to ensure we don't keep adding lookups from the - # same relationships to stop infinite recursion. So, if we - # are already on an automatically added lookup, don't add - # the new lookups from relationships we've seen already. - if not (lookup in auto_lookups and - descriptor in followed_descriptors): - for f in additional_prl: - new_prl = LOOKUP_SEP.join([current_lookup, f]) - auto_lookups.append(new_prl) - done_queries[current_lookup] = obj_list - followed_descriptors.add(descriptor) + obj_list, additional_prl = prefetch_one_level(obj_list, prefetcher, attr) + # We need to ensure we don't keep adding lookups from the + # same relationships to stop infinite recursion. So, if we + # are already on an automatically added lookup, don't add + # the new lookups from relationships we've seen already. + if not (lookup in auto_lookups and + descriptor in followed_descriptors): + for f in additional_prl: + new_prl = LOOKUP_SEP.join([current_lookup, f]) + auto_lookups.append(new_prl) + done_queries[current_lookup] = obj_list + followed_descriptors.add(descriptor) else: # Either a singly related object that has already been fetched # (e.g. via select_related), or hopefully some other property