summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSelwin Ong <selwin.ong@gmail.com>2013-05-21 18:35:12 +0300
committerAnssi Kääriäinen <akaariai@gmail.com>2013-05-21 18:52:28 +0300
commitea9a0857d4922fab1f9146f3a7828b67281edc89 (patch)
tree0e4b9751c8731a839f1dfeb06f6d63dc25b05eda
parentd595b61acae91e6111ec26cb0a4a1b1a9f4eb0d5 (diff)
Fixed #19326 -- Added first() and last() methods to QuerySet
-rw-r--r--django/db/models/manager.py6
-rw-r--r--django/db/models/query.py20
-rw-r--r--docs/ref/models/querysets.txt30
-rw-r--r--docs/releases/1.6.txt5
-rw-r--r--tests/get_earliest_or_latest/tests.py31
5 files changed, 92 insertions, 0 deletions
diff --git a/django/db/models/manager.py b/django/db/models/manager.py
index 43a8264f11..a1aa79f809 100644
--- a/django/db/models/manager.py
+++ b/django/db/models/manager.py
@@ -186,6 +186,12 @@ class Manager(six.with_metaclass(RenameManagerMethods)):
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)
diff --git a/django/db/models/query.py b/django/db/models/query.py
index 4313d044ee..f2015f57a8 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -498,6 +498,26 @@ class QuerySet(object):
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]
+ 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
diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
index 2dec00afc1..b9a4037440 100644
--- a/docs/ref/models/querysets.txt
+++ b/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
~~~~~~~~~
diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt
index a417e81f62..29b4569d4e 100644
--- a/docs/releases/1.6.txt
+++ b/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
=====================================
diff --git a/tests/get_earliest_or_latest/tests.py b/tests/get_earliest_or_latest/tests.py
index 6317a0974c..8d16af9587 100644
--- a/tests/get_earliest_or_latest/tests.py
+++ b/tests/get_earliest_or_latest/tests.py
@@ -121,3 +121,34 @@ class EarliestOrLatestTests(TestCase):
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)