Loading django/db/models/query.py +8 −10 Original line number Diff line number Diff line Loading @@ -516,20 +516,18 @@ class QuerySet(object): """ Returns the first object of a query, returns None if no match is found. """ qs = self if self.ordered else self.order_by('pk') try: return qs[0] except IndexError: objects = list((self if self.ordered else self.order_by('pk'))[:1]) if objects: return objects[0] return None def last(self): """ Returns the last object of a query, returns None if no match is found. """ qs = self.reverse() if self.ordered else self.order_by('-pk') try: return qs[0] except IndexError: objects = list((self.reverse() if self.ordered else self.order_by('-pk'))[:1]) if objects: return objects[0] return None def in_bulk(self, id_list): Loading tests/get_earliest_or_latest/models.py +15 −0 Original line number Diff line number Diff line Loading @@ -14,3 +14,18 @@ class Person(models.Model): name = models.CharField(max_length=30) birthday = models.DateField() # Note that this model doesn't have "get_latest_by" set. # Ticket #23555 - model with an intentionally broken QuerySet.__iter__ method. class IndexErrorQuerySet(models.QuerySet): """ Emulates the case when some internal code raises an unexpected IndexError. """ def __iter__(self): raise IndexError class IndexErrorArticle(Article): objects = IndexErrorQuerySet.as_manager() tests/get_earliest_or_latest/tests.py +25 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ from datetime import datetime from django.test import TestCase from .models import Article, Person from .models import Article, Person, IndexErrorArticle class EarliestOrLatestTests(TestCase): Loading Loading @@ -122,6 +122,9 @@ class EarliestOrLatestTests(TestCase): self.assertRaises(AssertionError, Person.objects.latest) self.assertEqual(Person.objects.latest("birthday"), p2) class TestFirstLast(TestCase): def test_first(self): p1 = Person.objects.create(name="Bob", birthday=datetime(1950, 1, 1)) p2 = Person.objects.create(name="Alice", birthday=datetime(1961, 2, 3)) Loading Loading @@ -152,3 +155,24 @@ class EarliestOrLatestTests(TestCase): self.assertIs( Person.objects.filter(birthday__lte=datetime(1940, 1, 1)).last(), None) def test_index_error_not_suppressed(self): """ #23555 -- Unexpected IndexError exceptions in QuerySet iteration shouldn't be suppressed. """ def check(): # We know that we've broken the __iter__ method, so the queryset # should always raise an exception. self.assertRaises(IndexError, lambda: IndexErrorArticle.objects.all()[0]) self.assertRaises(IndexError, IndexErrorArticle.objects.all().first) self.assertRaises(IndexError, IndexErrorArticle.objects.all().last) check() # And it does not matter if there are any records in the DB. IndexErrorArticle.objects.create( headline="Article 1", pub_date=datetime(2005, 7, 26), expire_date=datetime(2005, 9, 1) ) check() Loading
django/db/models/query.py +8 −10 Original line number Diff line number Diff line Loading @@ -516,20 +516,18 @@ class QuerySet(object): """ Returns the first object of a query, returns None if no match is found. """ qs = self if self.ordered else self.order_by('pk') try: return qs[0] except IndexError: objects = list((self if self.ordered else self.order_by('pk'))[:1]) if objects: return objects[0] return None def last(self): """ Returns the last object of a query, returns None if no match is found. """ qs = self.reverse() if self.ordered else self.order_by('-pk') try: return qs[0] except IndexError: objects = list((self.reverse() if self.ordered else self.order_by('-pk'))[:1]) if objects: return objects[0] return None def in_bulk(self, id_list): Loading
tests/get_earliest_or_latest/models.py +15 −0 Original line number Diff line number Diff line Loading @@ -14,3 +14,18 @@ class Person(models.Model): name = models.CharField(max_length=30) birthday = models.DateField() # Note that this model doesn't have "get_latest_by" set. # Ticket #23555 - model with an intentionally broken QuerySet.__iter__ method. class IndexErrorQuerySet(models.QuerySet): """ Emulates the case when some internal code raises an unexpected IndexError. """ def __iter__(self): raise IndexError class IndexErrorArticle(Article): objects = IndexErrorQuerySet.as_manager()
tests/get_earliest_or_latest/tests.py +25 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,7 @@ from datetime import datetime from django.test import TestCase from .models import Article, Person from .models import Article, Person, IndexErrorArticle class EarliestOrLatestTests(TestCase): Loading Loading @@ -122,6 +122,9 @@ class EarliestOrLatestTests(TestCase): self.assertRaises(AssertionError, Person.objects.latest) self.assertEqual(Person.objects.latest("birthday"), p2) class TestFirstLast(TestCase): def test_first(self): p1 = Person.objects.create(name="Bob", birthday=datetime(1950, 1, 1)) p2 = Person.objects.create(name="Alice", birthday=datetime(1961, 2, 3)) Loading Loading @@ -152,3 +155,24 @@ class EarliestOrLatestTests(TestCase): self.assertIs( Person.objects.filter(birthday__lte=datetime(1940, 1, 1)).last(), None) def test_index_error_not_suppressed(self): """ #23555 -- Unexpected IndexError exceptions in QuerySet iteration shouldn't be suppressed. """ def check(): # We know that we've broken the __iter__ method, so the queryset # should always raise an exception. self.assertRaises(IndexError, lambda: IndexErrorArticle.objects.all()[0]) self.assertRaises(IndexError, IndexErrorArticle.objects.all().first) self.assertRaises(IndexError, IndexErrorArticle.objects.all().last) check() # And it does not matter if there are any records in the DB. IndexErrorArticle.objects.create( headline="Article 1", pub_date=datetime(2005, 7, 26), expire_date=datetime(2005, 9, 1) ) check()