Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #19326 -- Added first() and last() methods to QuerySet

  • Loading branch information...
commit ea9a0857d4922fab1f9146f3a7828b67281edc89 1 parent d595b61
@selwin selwin authored akaariai committed
View
6 django/db/models/manager.py
@@ -186,6 +186,12 @@ def earliest(self, *args, **kwargs):
def latest(self, *args, **kwargs):
return self.get_queryset().latest(*args, **kwargs)
+ def first(self):
+ return self.get_queryset().first()
+
+ def last(self):
+ return self.get_queryset().last()
+
def order_by(self, *args, **kwargs):
return self.get_queryset().order_by(*args, **kwargs)
View
20 django/db/models/query.py
@@ -498,6 +498,26 @@ def earliest(self, field_name=None):
def latest(self, field_name=None):
return self._earliest_or_latest(field_name=field_name, direction="-")
+ def first(self):
+ """
+ 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]
@selwin
selwin added a note

@akaariai it seems like this line is missing [:1], is this intentional? If not, I'll submit a pull request correcting this issue. You can see the original implementation here.

@akaariai Collaborator

Yes, it is intentional. I didn't see why it is needed. The generated queryfor qs[0] is: SELECT ... FROM "get_earliest_or_latest_person" ORDER BY "get_earliest_or_latest_person"."id" ASC LIMIT 1, so that seems like it is correct.

@selwin
selwin added a note
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ except IndexError:
+ 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:
+ return None
+
def in_bulk(self, id_list):
"""
Returns a dictionary mapping each of the given IDs to the object with
View
30 docs/ref/models/querysets.txt
@@ -1585,6 +1585,36 @@ earliest
Works otherwise like :meth:`~django.db.models.query.QuerySet.latest` except
the direction is changed.
+first
+~~~~~
+.. method:: first()
+
+.. versionadded:: 1.6
+
+Returns the first object matched by the queryset, or ``None`` if there
+is no matching object. If the ``QuerySet`` has no ordering defined, then the
+queryset is automatically ordered by the primary key.
+
+Example::
+
+ p = Article.objects.order_by('title', 'pub_date').first()
+
+Note that ``first()`` is a convenience method, the following code sample is
+equivalent to the above example::
+
+ try:
+ p = Article.objects.order_by('title', 'pub_date')[0]
+ except IndexError:
+ p = None
+
+last
+~~~~
+.. method:: last()
+
+.. versionadded:: 1.6
+
+Works like :meth:`first()` except the ordering is reversed.
+
aggregate
~~~~~~~~~
View
5 docs/releases/1.6.txt
@@ -253,6 +253,11 @@ Minor features
allow users to specify the primary keys of objects they want to dump.
This option can only be used with one model.
+* Added ``QuerySet`` methods :meth:`~django.db.models.query.QuerySet.first`
+ and :meth:`~django.db.models.query.QuerySet.last` which are convenience
+ methods returning the first or last object matching the filters. Returns
+ ``None`` if there are no objects matching.
+
Backwards incompatible changes in 1.6
=====================================
View
31 tests/get_earliest_or_latest/tests.py
@@ -121,3 +121,34 @@ def test_latest_manual(self):
p2 = Person.objects.create(name="Stephanie", birthday=datetime(1960, 2, 3))
self.assertRaises(AssertionError, Person.objects.latest)
self.assertEqual(Person.objects.latest("birthday"), p2)
+
+ 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.assertIs(
+ Person.objects.filter(birthday__lte=datetime(1940, 1, 1)).first(),
+ None)
+
+ 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))
+ # Note: by default PK ordering.
+ 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.assertIs(
+ Person.objects.filter(birthday__lte=datetime(1940, 1, 1)).last(),
+ None)
Please sign in to comment.
Something went wrong with that request. Please try again.