1
0
mirror of https://github.com/django/django.git synced 2025-07-06 02:39:12 +00:00

queryset-refactor: Added some error checking for a potential crasher if model ordering is set up in a cycle somehow. The error reporting here isn't perfect (it doesn't give any hints about what the infinite loop might be), but it's better than nothing.

git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7046 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2008-01-28 16:08:34 +00:00
parent 3c490660c5
commit a214c6b86a
2 changed files with 32 additions and 2 deletions

View File

@ -507,7 +507,8 @@ class Query(object):
result.append('%s %s' % (elt, order)) result.append('%s %s' % (elt, order))
return result return result
def find_ordering_name(self, name, opts, alias=None, default_order='ASC'): def find_ordering_name(self, name, opts, alias=None, default_order='ASC',
already_seen=None):
""" """
Returns the table alias (the name might be ambiguous, the alias will Returns the table alias (the name might be ambiguous, the alias will
not be) and column name for ordering by the given 'name' parameter. not be) and column name for ordering by the given 'name' parameter.
@ -525,10 +526,18 @@ class Query(object):
# If we get to this point and the field is a relation to another model, # If we get to this point and the field is a relation to another model,
# append the default ordering for that model. # append the default ordering for that model.
if len(joins) > 1 and opts.ordering: if len(joins) > 1 and opts.ordering:
# Firstly, avoid infinite loops.
if not already_seen:
already_seen = {}
join_tuple = tuple([tuple(j) for j in joins])
if join_tuple in already_seen:
raise TypeError('Infinite loop caused by ordering.')
already_seen[join_tuple] = True
results = [] results = []
for item in opts.ordering: for item in opts.ordering:
results.extend(self.find_ordering_name(item, opts, alias, results.extend(self.find_ordering_name(item, opts, alias,
order)) order, already_seen))
return results return results
if alias: if alias:

View File

@ -97,6 +97,20 @@ class X(models.Model):
class Y(models.Model): class Y(models.Model):
x1 = models.ForeignKey(X, related_name='y1') x1 = models.ForeignKey(X, related_name='y1')
# Some models with a cycle in the default ordering. This would be bad if we
# didn't catch the infinite loop.
class LoopX(models.Model):
y = models.ForeignKey('LoopY')
class Meta:
ordering = ['y']
class LoopY(models.Model):
x = models.ForeignKey(LoopX)
class Meta:
ordering = ['x']
__test__ = {'API_TESTS':""" __test__ = {'API_TESTS':"""
>>> t1 = Tag(name='t1') >>> t1 = Tag(name='t1')
>>> t1.save() >>> t1.save()
@ -373,6 +387,13 @@ Bug #2076
>>> Cover.objects.all() >>> Cover.objects.all()
[<Cover: first>, <Cover: second>] [<Cover: first>, <Cover: second>]
# If you're not careful, it's possible to introduce infinite loops via default
# ordering on foreign keys in a cycle. We detect that.
>>> LoopX.objects.all()
Traceback (most recent call last):
...
TypeError: Infinite loop caused by ordering.
# If the remote model does not have a default ordering, we order by its 'id' # If the remote model does not have a default ordering, we order by its 'id'
# field. # field.
>>> Item.objects.order_by('creator', 'name') >>> Item.objects.order_by('creator', 'name')