tests for the earliest and latest objects methods classmethod def setupclasscls super setupclass cls articlegetlatestby article meta getlatestby def teardownself article meta getlatestby self articlegetlatestby def testearliestself because no articles exist yet earliest raises articledoesnotexist with self assertraisesarticle doesnotexist article objects earliest a1 article objects create headlinearticle 1 pubdatedatetime2005 7 26 expiredatedatetime2005 9 1 a2 article objects create headlinearticle 2 pubdatedatetime2005 7 27 expiredatedatetime2005 7 28 a3 article objects create headlinearticle 3 pubdatedatetime2005 7 28 expiredatedatetime2005 8 27 a4 article objects create headlinearticle 4 pubdatedatetime2005 7 28 expiredatedatetime2005 7 30 get the earliest article self assertequalarticle objects earliest a1 get the earliest article that matches certain filters self assertequal article objects filterpubdategtdatetime2005 7 26 earliest a2 pass a custom field name to earliest to change the field that s used to determine the earliest object self assertequalarticle objects earliestexpiredate a2 self assertequal article objects filterpubdategtdatetime2005 7 26 earliest expiredate a2 earliest overrides any other ordering specified on the query refs 11283 self assertequalarticle objects orderbyid earliest a1 error is raised if the user forgot to add a getlatestby in the model meta article objects model meta getlatestby none with self assertraisesmessage valueerror earliest and latest require either fields as positional arguments or getlatestby in the model s meta article objects earliest earliest publication date earliest expire date self assertequal article objects filterpubdatedatetime2005 7 28 earliest pubdate expiredate a4 earliest publication date latest expire date self assertequal article objects filterpubdatedatetime2005 7 28 earliest pubdate expiredate a3 meta getlatestby may be a tuple article objects model meta getlatestby pubdate expiredate self assertequal article objects filterpubdatedatetime2005 7 28 earliest a4 def testearliestslicedquerysetself msg cannot change a query once a slice has been taken with self assertraisesmessagetypeerror msg article objects all0 5 earliest def testlatestself because no articles exist yet latest raises articledoesnotexist with self assertraisesarticle doesnotexist article objects latest a1 article objects create headlinearticle 1 pubdatedatetime2005 7 26 expiredatedatetime2005 9 1 a2 article objects create headlinearticle 2 pubdatedatetime2005 7 27 expiredatedatetime2005 7 28 a3 article objects create headlinearticle 3 pubdatedatetime2005 7 27 expiredatedatetime2005 8 27 a4 article objects create headlinearticle 4 pubdatedatetime2005 7 28 expiredatedatetime2005 7 30 get the latest article self assertequalarticle objects latest a4 get the latest article that matches certain filters self assertequal article objects filterpubdateltdatetime2005 7 27 latest a1 pass a custom field name to latest to change the field that s used to determine the latest object self assertequalarticle objects latestexpiredate a1 self assertequal article objects filterpubdategtdatetime2005 7 26 latest expiredate a3 latest overrides any other ordering specified on the query 11283 self assertequalarticle objects orderbyid latest a4 error is raised if getlatestby isn t in model meta article objects model meta getlatestby none with self assertraisesmessage valueerror earliest and latest require either fields as positional arguments or getlatestby in the model s meta article objects latest latest publication date latest expire date self assertequal article objects filterpubdatedatetime2005 7 27 latest pubdate expiredate a3 latest publication date earliest expire date self assertequal article objects filterpubdatedatetime2005 7 27 latest pubdate expiredate a2 meta getlatestby may be a tuple article objects model meta getlatestby pubdate expiredate self assertequal article objects filterpubdatedatetime2005 7 27 latest a3 def testlatestslicedquerysetself msg cannot change a query once a slice has been taken with self assertraisesmessagetypeerror msg article objects all0 5 latest def testlatestmanualself you can still use latest with a model that doesn t have getlatestby set just pass in the field name manually person objects createnameralph birthdaydatetime1950 1 1 p2 person objects createnamestephanie birthdaydatetime1960 2 3 msg earliest and latest require either fields as positional arguments or getlatestby in the model s meta with self assertraisesmessagevalueerror msg person objects latest self assertequalperson objects latestbirthday p2 class testfirstlasttestcase def testfirstself p1 person objects createnamebob birthdaydatetime1950 1 1 p2 person objects createnamealice birthdaydatetime1961 2 3 self assertequalperson objects first p1 self assertequalperson objects orderbyname first p2 self assertequal person objects filterbirthdayltedatetime1955 1 1 first p1 self assertisnone person objects filterbirthdayltedatetime1940 1 1 first def testlastself p1 person objects createnamealice birthdaydatetime1950 1 1 p2 person objects createnamebob birthdaydatetime1960 2 3 note by default pk ordering self assertequalperson objects last p2 self assertequalperson objects orderbyname last p1 self assertequal person objects filterbirthdayltedatetime1955 1 1 last p1 self assertisnone person objects filterbirthdayltedatetime1940 1 1 last def testindexerrornotsuppressedself 23555 unexpected indexerror exceptions in queryset iteration def check we know that we ve broken the iter method so the queryset should always raise an exception with self assertraisesindexerror indexerrorarticle objects all 10 2 with self assertraisesindexerror indexerrorarticle objects first with self assertraisesindexerror indexerrorarticle objects last check and it does not matter if there are any records in the db indexerrorarticle objects create headlinearticle 1 pubdatedatetime2005 7 26 expiredatedatetime2005 9 1 check def testfirstlastunorderedqsaggregationerrorself a1 article objects create headlinearticle 1 pubdatedatetime2005 7 26 expiredatedatetime2005 9 1 comment objects createarticlea1 likescount5 qs comment objects valuesarticle annotateavglikesavglikescount msg cannot use queryset s on an unordered queryset performing aggregation add an ordering with orderby with self assertraisesmessagetypeerror msg first qs first with self assertraisesmessagetypeerror msg last qs last tests for the earliest and latest objects methods because no articles exist yet earliest raises articledoesnotexist get the earliest article get the earliest article that matches certain filters pass a custom field name to earliest to change the field that s used to determine the earliest object earliest overrides any other ordering specified on the query refs 11283 error is raised if the user forgot to add a get_latest_by in the model meta earliest publication date earliest expire date earliest publication date latest expire date meta get_latest_by may be a tuple because no articles exist yet latest raises articledoesnotexist get the latest article get the latest article that matches certain filters pass a custom field name to latest to change the field that s used to determine the latest object latest overrides any other ordering specified on the query 11283 error is raised if get_latest_by isn t in model meta latest publication date latest expire date latest publication date earliest expire date meta get_latest_by may be a tuple you can still use latest with a model that doesn t have get_latest_by set just pass in the field name manually note by default pk ordering 23555 unexpected indexerror exceptions in queryset iteration shouldn t be suppressed we know that we ve broken the __iter__ method so the queryset should always raise an exception and it does not matter if there are any records in the db
from datetime import datetime

from django.db.models import Avg
from django.test import TestCase

from .models import Article, Comment, IndexErrorArticle, Person


class EarliestOrLatestTests(TestCase):
    

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls._article_get_latest_by = Article._meta.get_latest_by

    def tearDown(self):
        Article._meta.get_latest_by = self._article_get_latest_by

    def test_earliest(self):
        
        with self.assertRaises(Article.DoesNotExist):
            Article.objects.earliest()

        a1 = Article.objects.create(
            headline="Article 1",
            pub_date=datetime(2005, 7, 26),
            expire_date=datetime(2005, 9, 1),
        )
        a2 = Article.objects.create(
            headline="Article 2",
            pub_date=datetime(2005, 7, 27),
            expire_date=datetime(2005, 7, 28),
        )
        a3 = Article.objects.create(
            headline="Article 3",
            pub_date=datetime(2005, 7, 28),
            expire_date=datetime(2005, 8, 27),
        )
        a4 = Article.objects.create(
            headline="Article 4",
            pub_date=datetime(2005, 7, 28),
            expire_date=datetime(2005, 7, 30),
        )

        
        self.assertEqual(Article.objects.earliest(), a1)
        
        self.assertEqual(
            Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).earliest(), a2
        )

        
        
        self.assertEqual(Article.objects.earliest("expire_date"), a2)
        self.assertEqual(
            Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).earliest(
                "expire_date"
            ),
            a2,
        )

        
        
        self.assertEqual(Article.objects.order_by("id").earliest(), a1)

        
        
        Article.objects.model._meta.get_latest_by = None
        with self.assertRaisesMessage(
            ValueError,
            "earliest() and latest() require either fields as positional "
            "arguments or 'get_latest_by' in the model's Meta.",
        ):
            Article.objects.earliest()

        
        self.assertEqual(
            Article.objects.filter(pub_date=datetime(2005, 7, 28)).earliest(
                "pub_date", "expire_date"
            ),
            a4,
        )
        
        self.assertEqual(
            Article.objects.filter(pub_date=datetime(2005, 7, 28)).earliest(
                "pub_date", "-expire_date"
            ),
            a3,
        )

        
        Article.objects.model._meta.get_latest_by = ("pub_date", "expire_date")
        self.assertEqual(
            Article.objects.filter(pub_date=datetime(2005, 7, 28)).earliest(), a4
        )

    def test_earliest_sliced_queryset(self):
        msg = "Cannot change a query once a slice has been taken."
        with self.assertRaisesMessage(TypeError, msg):
            Article.objects.all()[0:5].earliest()

    def test_latest(self):
        
        with self.assertRaises(Article.DoesNotExist):
            Article.objects.latest()

        a1 = Article.objects.create(
            headline="Article 1",
            pub_date=datetime(2005, 7, 26),
            expire_date=datetime(2005, 9, 1),
        )
        a2 = Article.objects.create(
            headline="Article 2",
            pub_date=datetime(2005, 7, 27),
            expire_date=datetime(2005, 7, 28),
        )
        a3 = Article.objects.create(
            headline="Article 3",
            pub_date=datetime(2005, 7, 27),
            expire_date=datetime(2005, 8, 27),
        )
        a4 = Article.objects.create(
            headline="Article 4",
            pub_date=datetime(2005, 7, 28),
            expire_date=datetime(2005, 7, 30),
        )

        
        self.assertEqual(Article.objects.latest(), a4)
        
        self.assertEqual(
            Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest(), a1
        )

        
        
        self.assertEqual(Article.objects.latest("expire_date"), a1)
        self.assertEqual(
            Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest(
                "expire_date"
            ),
            a3,
        )

        
        self.assertEqual(Article.objects.order_by("id").latest(), a4)

        
        Article.objects.model._meta.get_latest_by = None
        with self.assertRaisesMessage(
            ValueError,
            "earliest() and latest() require either fields as positional "
            "arguments or 'get_latest_by' in the model's Meta.",
        ):
            Article.objects.latest()

        
        self.assertEqual(
            Article.objects.filter(pub_date=datetime(2005, 7, 27)).latest(
                "pub_date", "expire_date"
            ),
            a3,
        )
        
        self.assertEqual(
            Article.objects.filter(pub_date=datetime(2005, 7, 27)).latest(
                "pub_date", "-expire_date"
            ),
            a2,
        )

        
        Article.objects.model._meta.get_latest_by = ("pub_date", "expire_date")
        self.assertEqual(
            Article.objects.filter(pub_date=datetime(2005, 7, 27)).latest(), a3
        )

    def test_latest_sliced_queryset(self):
        msg = "Cannot change a query once a slice has been taken."
        with self.assertRaisesMessage(TypeError, msg):
            Article.objects.all()[0:5].latest()

    def test_latest_manual(self):
        
        
        Person.objects.create(name="Ralph", birthday=datetime(1950, 1, 1))
        p2 = Person.objects.create(name="Stephanie", birthday=datetime(1960, 2, 3))
        msg = (
            "earliest() and latest() require either fields as positional arguments "
            "or 'get_latest_by' in the model's Meta."
        )
        with self.assertRaisesMessage(ValueError, msg):
            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))
        self.assertEqual(Person.objects.first(), p1)
        self.assertEqual(Person.objects.order_by("name").first(), p2)
        self.assertEqual(
            Person.objects.filter(birthday__lte=datetime(1955, 1, 1)).first(), p1
        )
        self.assertIsNone(
            Person.objects.filter(birthday__lte=datetime(1940, 1, 1)).first()
        )

    def test_last(self):
        p1 = Person.objects.create(name="Alice", birthday=datetime(1950, 1, 1))
        p2 = Person.objects.create(name="Bob", birthday=datetime(1960, 2, 3))
        
        self.assertEqual(Person.objects.last(), p2)
        self.assertEqual(Person.objects.order_by("-name").last(), p1)
        self.assertEqual(
            Person.objects.filter(birthday__lte=datetime(1955, 1, 1)).last(), p1
        )
        self.assertIsNone(
            Person.objects.filter(birthday__lte=datetime(1940, 1, 1)).last()
        )

    def test_index_error_not_suppressed(self):
        

        def check():
            
            
            with self.assertRaises(IndexError):
                IndexErrorArticle.objects.all()[:10:2]
            with self.assertRaises(IndexError):
                IndexErrorArticle.objects.first()
            with self.assertRaises(IndexError):
                IndexErrorArticle.objects.last()

        check()

        
        IndexErrorArticle.objects.create(
            headline="Article 1",
            pub_date=datetime(2005, 7, 26),
            expire_date=datetime(2005, 9, 1),
        )
        check()

    def test_first_last_unordered_qs_aggregation_error(self):
        a1 = Article.objects.create(
            headline="Article 1",
            pub_date=datetime(2005, 7, 26),
            expire_date=datetime(2005, 9, 1),
        )
        Comment.objects.create(article=a1, likes_count=5)

        qs = Comment.objects.values("article").annotate(avg_likes=Avg("likes_count"))
        msg = (
            "Cannot use QuerySet.%s() on an unordered queryset performing aggregation. "
            "Add an ordering with order_by()."
        )
        with self.assertRaisesMessage(TypeError, msg % "first"):
            qs.first()
        with self.assertRaisesMessage(TypeError, msg % "last"):
            qs.last()