Ticket #17014: prefetch_recursion_trunk.diff

File prefetch_recursion_trunk.diff, 4.6 KB (added by Anssi Kääriäinen, 13 years ago)
  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    index b21db2e..cb79382 100644
    a b REPR_OUTPUT_SIZE = 20  
    2323# Pull into this namespace for backwards compatibility.
    2424EmptyResultSet = sql.EmptyResultSet
    2525
     26# Maximum amount of prefetch related queries to be done when evaluating
     27# a single queryset.
     28MAX_PREFETCH_PER_QUERY = 100
     29
    2630class QuerySet(object):
    2731    """
    2832    Represents a lazy database lookup for a set of objects.
    def prefetch_related_objects(result_cache, related_lookups):  
    15781582
    15791583    # We may expand related_lookups, so need a loop that allows for that
    15801584    for lookup in related_lookups:
     1585        if len(done_queries) > MAX_PREFETCH_PER_QUERY:
     1586            raise ValueError(
     1587                'Over %s prefetch_related lookups in one query. '
     1588                'Likely reason is recursion caused by default managers'
     1589                % MAX_PREFETCH_PER_QUERY
     1590            )
    15811591        if lookup in done_lookups:
    15821592            # We've done exactly this already, skip the whole thing
    15831593            continue
  • tests/modeltests/prefetch_related/models.py

    diff --git a/tests/modeltests/prefetch_related/models.py b/tests/modeltests/prefetch_related/models.py
    index ab28496..8ea01a0 100644
    a b class Employee(models.Model):  
    163163
    164164    class Meta:
    165165        ordering = ['id']
     166
     167class RecursiveManager(models.Manager):
     168    def get_query_set(self):
     169        return (super(RecursiveManager, self).get_query_set()
     170                .prefetch_related('recurse_reverse'))
     171
     172class RecursiveRel(models.Model):
     173    val = models.TextField()
     174    recursion = models.ForeignKey('self', related_name='recurse_reverse', null=True)
     175    objects = RecursiveManager()
     176
     177"""
     178Construct a nasty diamond strucure of models,
     179connected by default managers:
     180       B
     181      / \
     182     /   \
     183    /     \
     184   A <---- D
     185    \     /
     186     \   /
     187      \ /
     188       C
     189
     190Contains two loops:
     191A -> C -> D -> A
     192A -> B -> D -> A
     193
     194In addition, C -> D and B -> D have same accessor name, d_set
     195"""
     196
     197class AManager(models.Manager):
     198    def get_query_set(self):
     199        return super(AManager, self).get_query_set().prefetch_related('b_set', 'c_set')
     200
     201class BManager(models.Manager):
     202    def get_query_set(self):
     203        return super(BManager, self).get_query_set().prefetch_related('d_set')
     204
     205class CManager(models.Manager):
     206    def get_query_set(self):
     207        return super(CManager, self).get_query_set().prefetch_related('d_set')
     208
     209class DManager(models.Manager):
     210    def get_query_set(self):
     211        return super(CManager, self).get_query_set().prefetch_related('a_set')
     212
     213class DiamondA(models.Model):
     214    d = models.ForeignKey('DiamondD', null=True)
     215    objects = AManager()
     216
     217class DiamondB(models.Model):
     218    a = models.ForeignKey(DiamondA, null=True)
     219    objects = BManager()
     220
     221class DiamondC(models.Model):
     222    a = models.ForeignKey(DiamondA, null=True)
     223    objects = CManager()
     224
     225class DiamondD(models.Model):
     226    b = models.ForeignKey(DiamondB, null=True)
     227    c = models.ForeignKey(DiamondC, null=True)
     228    objects = DManager()
  • tests/modeltests/prefetch_related/tests.py

    diff --git a/tests/modeltests/prefetch_related/tests.py b/tests/modeltests/prefetch_related/tests.py
    index 45202f2..c991fc3 100644
    a b from django.utils import unittest  
    77from models import (Author, Book, Reader, Qualification, Teacher, Department,
    88                    TaggedItem, Bookmark, AuthorAddress, FavoriteAuthors,
    99                    AuthorWithAge, BookWithYear, Person, House, Room,
    10                     Employee)
     10                    Employee, RecursiveRel, DiamondA, DiamondB, DiamondC,
     11                    DiamondD)
    1112
    1213
    1314class PrefetchRelatedTests(TestCase):
    class NullableTest(TestCase):  
    416417                        for e in qs2]
    417418
    418419        self.assertEqual(co_serfs, co_serfs2)
     420
     421
     422class RecursionTest(TestCase):
     423    def setUp(self):
     424        r1 = RecursiveRel.objects.create(val='foo')
     425        r2 = RecursiveRel.objects.create(val='foo', recursion=r1)
     426        r1.recursion = r2
     427        r1.save()
     428
     429    def test_recursion(self):
     430        RecursiveRel.objects.all()[0]
     431
     432class DiamondTest(TestCase):
     433    def setUp(self):
     434        a = DiamondA.objects.create()
     435        b = DiamondB.objects.create(a=a)
     436        c = DiamondA.objects.create(a=a)
     437        d = DiamondA.objects.create(b=b, c=c)
     438        a.d = d
     439        a.save()
     440
     441    def test_diamond_recusion(self):
     442        DiamondA.objects.all()[0]
Back to Top