Ticket #17813: t17813.4.diff

File t17813.4.diff, 16.8 KB (added by Adrien Lemaire, 12 years ago)
  • django/db/models/manager.py

    diff --git a/django/db/models/manager.py b/django/db/models/manager.py
    index e1bbf6e..abff034 100644
    a b def ensure_default_manager(sender, **kwargs):  
    4242
    4343signals.class_prepared.connect(ensure_default_manager)
    4444
     45
    4546class Manager(object):
    4647    # Tracks each time a Manager instance is created. Used to retain order.
    4748    creation_counter = 0
    class Manager(object):  
    160161    def iterator(self, *args, **kwargs):
    161162        return self.get_query_set().iterator(*args, **kwargs)
    162163
     164    def earliest(self, *args, **kwargs):
     165        return self.get_query_set().earliest(*args, **kwargs)
     166
    163167    def latest(self, *args, **kwargs):
    164168        return self.get_query_set().latest(*args, **kwargs)
    165169
    class Manager(object):  
    208212    def raw(self, raw_query, params=None, *args, **kwargs):
    209213        return RawQuerySet(raw_query=raw_query, model=self.model, params=params, using=self._db, *args, **kwargs)
    210214
     215
    211216class ManagerDescriptor(object):
    212217    # This class ensures managers aren't accessible via model instances.
    213218    # For example, Poll.objects works, but poll_obj.objects raises AttributeError.
    class ManagerDescriptor(object):  
    219224            raise AttributeError("Manager isn't accessible via %s instances" % type.__name__)
    220225        return self.manager
    221226
     227
    222228class EmptyManager(Manager):
    223229    def get_query_set(self):
    224230        return self.get_empty_query_set()
  • django/db/models/query.py

    diff --git a/django/db/models/query.py b/django/db/models/query.py
    index 44acadf..d1319d0 100644
    a b REPR_OUTPUT_SIZE = 20  
    2525# Pull into this namespace for backwards compatibility.
    2626EmptyResultSet = sql.EmptyResultSet
    2727
     28
    2829class QuerySet(object):
    2930    """
    3031    Represents a lazy database lookup for a set of objects.
    class QuerySet(object):  
    458459                    # Re-raise the IntegrityError with its original traceback.
    459460                    raise exc_info[1], None, exc_info[2]
    460461
    461     def latest(self, field_name=None):
     462    def _earliest_or_latest(self, field_name=None, direction="-"):
    462463        """
    463         Returns the latest object, according to the model's 'get_latest_by'
    464         option or optional given field_name.
     464        Returns the latest object, according to the model's
     465        'get_latest_by' option or optional given field_name.
    465466        """
    466         latest_by = field_name or self.model._meta.get_latest_by
    467         assert bool(latest_by), "latest() requires either a field_name parameter or 'get_latest_by' in the model"
     467        order_by = field_name or getattr(self.model._meta, 'get_latest_by')
     468        assert bool(order_by), "_earliest_or_latest() requires either a "\
     469            "field_name parameter or 'get_latest_by' in the model"
    468470        assert self.query.can_filter(), \
    469471                "Cannot change a query once a slice has been taken."
    470472        obj = self._clone()
    471473        obj.query.set_limits(high=1)
    472474        obj.query.clear_ordering()
    473         obj.query.add_ordering('-%s' % latest_by)
     475        obj.query.add_ordering('%s%s' % (direction, order_by))
    474476        return obj.get()
    475477
     478    def earliest(self, field_name=None):
     479        return self._earliest_or_latest(field_name=field_name, direction="")
     480
     481    def latest(self, field_name=None):
     482        return self._earliest_or_latest(field_name=field_name, direction="-")
     483
    476484    def in_bulk(self, id_list):
    477485        """
    478486        Returns a dictionary mapping each of the given IDs to the object with
    class EmptyQuerySet(QuerySet):  
    12371245    # situations).
    12381246    value_annotation = False
    12391247
     1248
    12401249def get_klass_info(klass, max_depth=0, cur_depth=0, requested=None,
    12411250                   only_load=None, local_only=False):
    12421251    """
  • docs/ref/models/options.txt

    diff --git a/docs/ref/models/options.txt b/docs/ref/models/options.txt
    index 6ca3d3b..3e77b8b 100644
    a b Django quotes column and table names behind the scenes.  
    7878    setting, if set. If the backend doesn't support tablespaces, this option is
    7979    ignored.
    8080
     81
    8182``get_latest_by``
    8283-----------------
    8384
  • docs/ref/models/querysets.txt

    diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt
    index b20c6e3..3664e61 100644
    a b This example returns the latest ``Entry`` in the table, according to the  
    14661466
    14671467If your model's :ref:`Meta <meta-options>` specifies
    14681468:attr:`~django.db.models.Options.get_latest_by`, you can leave off the
    1469 ``field_name`` argument to ``latest()``. Django will use the field specified
    1470 in :attr:`~django.db.models.Options.get_latest_by` by default.
     1469``field_name`` argument to ``earliest()`` or ``latest()``. Django will use the
     1470field specified in :attr:`~django.db.models.Options.get_latest_by` by default.
    14711471
    1472 Like :meth:`get()`, ``latest()`` raises
    1473 :exc:`~django.core.exceptions.DoesNotExist` if there is no object with the given
    1474 parameters.
     1472Like :meth:`get()`, ``earliest()`` and ``latest()`` raise
     1473:exc:`~django.core.exceptions.DoesNotExist` if there is no object with the
     1474given parameters.
    14751475
    1476 Note ``latest()`` exists purely for convenience and readability.
     1476Note that ``earliest()`` and ``latest()`` exist purely for convenience and
     1477readability.
    14771478
    14781479aggregate
    14791480~~~~~~~~~
  • docs/topics/db/models.txt

    diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt
    index ee6644f..586820e 100644
    a b right).  
    983983
    984984So a child model does not have access to its parent's :ref:`Meta
    985985<meta-options>` class. However, there are a few limited cases where the child
    986 inherits behavior from the parent: if the child does not specify an
    987 :attr:`~django.db.models.Options.ordering` attribute or a
    988 :attr:`~django.db.models.Options.get_latest_by` attribute, it will inherit
    989 these from its parent.
     986inherits behavior from the parent: if the child does not specify an attribute
     987:attr:`~django.db.models.Options.ordering` or
     988:attr:`~django.db.models.Options.get_latest_by`,
     989, it will inherit these from its parent.
    990990
    991991If the parent has an ordering and you don't want the child to have any natural
    992992ordering, you can explicitly disable it::
  • new file tests/modeltests/get_earliest_or_latest/models.py

    diff --git a/tests/modeltests/get_earliest_or_latest/.models.py.swp b/tests/modeltests/get_earliest_or_latest/.models.py.swp
    new file mode 100644
    index 0000000..53ea145
    Binary files /dev/null and b/tests/modeltests/get_earliest_or_latest/.models.py.swp differ
    diff --git a/tests/modeltests/get_earliest_or_latest/.tests.py.swp b/tests/modeltests/get_earliest_or_latest/.tests.py.swp
    new file mode 100644
    index 0000000..d33e29f
    Binary files /dev/null and b/tests/modeltests/get_earliest_or_latest/.tests.py.swp differ
    diff --git a/tests/modeltests/get_earliest_or_latest/__init__.py b/tests/modeltests/get_earliest_or_latest/__init__.py
    new file mode 100644
    index 0000000..e69de29
    diff --git a/tests/modeltests/get_earliest_or_latest/models.py b/tests/modeltests/get_earliest_or_latest/models.py
    new file mode 100644
    index 0000000..2453eaa
    - +  
     1"""
     28. get_latest_by
     3
     4Models can have a ``get_latest_by`` attribute, which should be set to the name
     5of a ``DateField`` or ``DateTimeField``. If ``get_latest_by`` exists, the
     6model's manager will get a ``latest()`` method, which will return the latest
     7object in the database according to that field. "Latest" means "having the date
     8farthest into the future."
     9"""
     10
     11from django.db import models
     12
     13
     14class Article(models.Model):
     15    headline = models.CharField(max_length=100)
     16    pub_date = models.DateField()
     17    expire_date = models.DateField()
     18    class Meta:
     19        get_latest_by = 'pub_date'
     20
     21    def __unicode__(self):
     22        return self.headline
     23
     24
     25class Person(models.Model):
     26    name = models.CharField(max_length=30)
     27    birthday = models.DateField()
     28
     29    # Note that this model doesn't have "get_latest_by" set.
     30
     31    def __unicode__(self):
     32        return self.name
  • new file tests/modeltests/get_earliest_or_latest/tests.py

    diff --git a/tests/modeltests/get_earliest_or_latest/tests.py b/tests/modeltests/get_earliest_or_latest/tests.py
    new file mode 100644
    index 0000000..bd27e13
    - +  
     1from __future__ import absolute_import
     2
     3from datetime import datetime
     4
     5from django.test import TestCase
     6
     7from .models import Article, Person
     8
     9
     10class EarliestOrLatestTests(TestCase):
     11    """Tests for the earliest() and latest() objects methods"""
     12
     13    def tearDown(self):
     14        """Makes sure Article has a get_latest_by"""
     15        if not Article._meta.get_latest_by:
     16            Article._meta.get_latest_by = 'pub_date'
     17
     18    def test_earliest(self):
     19        # Because no Articles exist yet, earliest() raises ArticleDoesNotExist.
     20        self.assertRaises(Article.DoesNotExist, Article.objects.earliest)
     21
     22        a1 = Article.objects.create(
     23            headline="Article 1", pub_date=datetime(2005, 7, 26),
     24            expire_date=datetime(2005, 9, 1)
     25        )
     26        a2 = Article.objects.create(
     27            headline="Article 2", pub_date=datetime(2005, 7, 27),
     28            expire_date=datetime(2005, 7, 28)
     29        )
     30        a3 = Article.objects.create(
     31            headline="Article 3", pub_date=datetime(2005, 7, 28),
     32            expire_date=datetime(2005, 8, 27)
     33        )
     34        a4 = Article.objects.create(
     35            headline="Article 4", pub_date=datetime(2005, 7, 28),
     36            expire_date=datetime(2005, 7, 30)
     37        )
     38
     39        # Get the earliest Article.
     40        self.assertEqual(Article.objects.earliest(), a1)
     41        # Get the earliest Article that matches certain filters.
     42        self.assertEqual(
     43            Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).earliest(),
     44            a2
     45        )
     46
     47        # Pass a custom field name to earliest() to change the field that's used
     48        # to determine the earliest object.
     49        self.assertEqual(Article.objects.earliest('expire_date'), a2)
     50        self.assertEqual(Article.objects.filter(
     51            pub_date__gt=datetime(2005, 7, 26)).earliest('expire_date'), a2)
     52
     53        # Ensure that earliest() overrides any other ordering specified on the
     54        # query. Refs #11283.
     55        self.assertEqual(Article.objects.order_by('id').earliest(), a1)
     56
     57        # Ensure that error is raised if the user forgot to add a get_latest_by
     58        # in the Model.Meta
     59        Article.objects.model._meta.get_latest_by = None
     60        self.assertRaisesMessage(
     61            AssertionError,
     62            "_earliest_or_latest() requires either a field_name parameter or "\
     63                "'get_latest_by' in the model",
     64            lambda: Article.objects.earliest(),
     65        )
     66
     67
     68    def test_latest(self):
     69        # Because no Articles exist yet, latest() raises ArticleDoesNotExist.
     70        self.assertRaises(Article.DoesNotExist, Article.objects.latest)
     71
     72        a1 = Article.objects.create(
     73            headline="Article 1", pub_date=datetime(2005, 7, 26),
     74            expire_date=datetime(2005, 9, 1)
     75        )
     76        a2 = Article.objects.create(
     77            headline="Article 2", pub_date=datetime(2005, 7, 27),
     78            expire_date=datetime(2005, 7, 28)
     79        )
     80        a3 = Article.objects.create(
     81            headline="Article 3", pub_date=datetime(2005, 7, 27),
     82            expire_date=datetime(2005, 8, 27)
     83        )
     84        a4 = Article.objects.create(
     85            headline="Article 4", pub_date=datetime(2005, 7, 28),
     86            expire_date=datetime(2005, 7, 30)
     87        )
     88
     89        # Get the latest Article.
     90        self.assertEqual(Article.objects.latest(), a4)
     91        # Get the latest Article that matches certain filters.
     92        self.assertEqual(
     93            Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest(),
     94            a1
     95        )
     96
     97        # Pass a custom field name to latest() to change the field that's used
     98        # to determine the latest object.
     99        self.assertEqual(Article.objects.latest('expire_date'), a1)
     100        self.assertEqual(
     101            Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest('expire_date'),
     102            a3,
     103        )
     104
     105        # Ensure that latest() overrides any other ordering specified on the query. Refs #11283.
     106        self.assertEqual(Article.objects.order_by('id').latest(), a4)
     107
     108        # Ensure that error is raised if the user forgot to add a get_latest_by
     109        # in the Model.Meta
     110        Article.objects.model._meta.get_latest_by = None
     111        self.assertRaisesMessage(
     112            AssertionError,
     113            "_earliest_or_latest() requires either a field_name parameter or "\
     114                "'get_latest_by' in the model",
     115            lambda: Article.objects.latest(),
     116        )
     117
     118
     119    def test_latest_manual(self):
     120        # You can still use latest() with a model that doesn't have
     121        # "get_latest_by" set -- just pass in the field name manually.
     122        p1 = Person.objects.create(name="Ralph", birthday=datetime(1950, 1, 1))
     123        p2 = Person.objects.create(name="Stephanie", birthday=datetime(1960, 2, 3))
     124        self.assertRaises(AssertionError, Person.objects.latest)
     125
     126        self.assertEqual(Person.objects.latest("birthday"), p2)
  • deleted file tests/modeltests/get_latest/models.py

    diff --git a/tests/modeltests/get_latest/__init__.py b/tests/modeltests/get_latest/__init__.py
    deleted file mode 100644
    index e69de29..0000000
    diff --git a/tests/modeltests/get_latest/models.py b/tests/modeltests/get_latest/models.py
    deleted file mode 100644
    index d8a690f..0000000
    + -  
    1 """
    2 8. get_latest_by
    3 
    4 Models can have a ``get_latest_by`` attribute, which should be set to the name
    5 of a ``DateField`` or ``DateTimeField``. If ``get_latest_by`` exists, the
    6 model's manager will get a ``latest()`` method, which will return the latest
    7 object in the database according to that field. "Latest" means "having the date
    8 farthest into the future."
    9 """
    10 
    11 from django.db import models
    12 
    13 
    14 class Article(models.Model):
    15     headline = models.CharField(max_length=100)
    16     pub_date = models.DateField()
    17     expire_date = models.DateField()
    18     class Meta:
    19         get_latest_by = 'pub_date'
    20 
    21     def __unicode__(self):
    22         return self.headline
    23 
    24 class Person(models.Model):
    25     name = models.CharField(max_length=30)
    26     birthday = models.DateField()
    27 
    28     # Note that this model doesn't have "get_latest_by" set.
    29 
    30     def __unicode__(self):
    31         return self.name
  • deleted file tests/modeltests/get_latest/tests.py

    diff --git a/tests/modeltests/get_latest/tests.py b/tests/modeltests/get_latest/tests.py
    deleted file mode 100644
    index 948af60..0000000
    + -  
    1 from __future__ import absolute_import
    2 
    3 from datetime import datetime
    4 
    5 from django.test import TestCase
    6 
    7 from .models import Article, Person
    8 
    9 
    10 class LatestTests(TestCase):
    11     def test_latest(self):
    12         # Because no Articles exist yet, latest() raises ArticleDoesNotExist.
    13         self.assertRaises(Article.DoesNotExist, Article.objects.latest)
    14 
    15         a1 = Article.objects.create(
    16             headline="Article 1", pub_date=datetime(2005, 7, 26),
    17             expire_date=datetime(2005, 9, 1)
    18         )
    19         a2 = Article.objects.create(
    20             headline="Article 2", pub_date=datetime(2005, 7, 27),
    21             expire_date=datetime(2005, 7, 28)
    22         )
    23         a3 = Article.objects.create(
    24             headline="Article 3", pub_date=datetime(2005, 7, 27),
    25             expire_date=datetime(2005, 8, 27)
    26         )
    27         a4 = Article.objects.create(
    28             headline="Article 4", pub_date=datetime(2005, 7, 28),
    29             expire_date=datetime(2005, 7, 30)
    30         )
    31 
    32         # Get the latest Article.
    33         self.assertEqual(Article.objects.latest(), a4)
    34         # Get the latest Article that matches certain filters.
    35         self.assertEqual(
    36             Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest(),
    37             a1
    38         )
    39 
    40         # Pass a custom field name to latest() to change the field that's used
    41         # to determine the latest object.
    42         self.assertEqual(Article.objects.latest('expire_date'), a1)
    43         self.assertEqual(
    44             Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest('expire_date'),
    45             a3,
    46         )
    47 
    48         # Ensure that latest() overrides any other ordering specified on the query. Refs #11283.
    49         self.assertEqual(Article.objects.order_by('id').latest(), a4)
    50 
    51     def test_latest_manual(self):
    52         # You can still use latest() with a model that doesn't have
    53         # "get_latest_by" set -- just pass in the field name manually.
    54         p1 = Person.objects.create(name="Ralph", birthday=datetime(1950, 1, 1))
    55         p2 = Person.objects.create(name="Stephanie", birthday=datetime(1960, 2, 3))
    56         self.assertRaises(AssertionError, Person.objects.latest)
    57 
    58         self.assertEqual(Person.objects.latest("birthday"), p2)
Back to Top