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:
parent
3c490660c5
commit
a214c6b86a
@ -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:
|
||||||
|
@ -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')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user