Ticket #17813: t17813.3.2.diff

File t17813.3.2.diff, 12.4 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 e25bea0..34e3fac 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 9b29e1e..85dbfea 100644
    a b right).  
    973973
    974974So a child model does not have access to its parent's :ref:`Meta
    975975<meta-options>` class. However, there are a few limited cases where the child
    976 inherits behavior from the parent: if the child does not specify an
    977 :attr:`~django.db.models.Options.ordering` attribute or a
    978 :attr:`~django.db.models.Options.get_latest_by` attribute, it will inherit
    979 these from its parent.
     976inherits behavior from the parent: if the child does not specify an attribute
     977:attr:`~django.db.models.Options.ordering` or
     978:attr:`~django.db.models.Options.get_latest_by`,
     979, it will inherit these from its parent.
    980980
    981981If the parent has an ordering and you don't want the child to have any natural
    982982ordering, you can explicitly disable it::
  • new file tests/modeltests/get_earliest_or_latest/models.py

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