From cbc144b375841507646d0850725b8add5a06382d Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sat, 4 Feb 2006 00:07:03 +0000 Subject: [PATCH] magic-removal: Added support for QuerySet.latest() and Manager.latest(), and updated unit tests. git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2260 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/db/models/fields/related.py | 11 ------ django/db/models/manager.py | 14 ++------ django/db/models/query.py | 9 +++++ tests/modeltests/get_latest/models.py | 52 ++++++++++++++++++++++----- 4 files changed, 56 insertions(+), 30 deletions(-) diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 5a35c6e861..1ca6d0e92d 100644 --- a/django/db/models/fields/related.py +++ b/django/db/models/fields/related.py @@ -126,12 +126,7 @@ class ManyRelatedObjectsDescriptor(object): else: manager.core_filters = {'%s__%s__exact' % (rel_field.name, instance_type._meta.pk.name): instance._get_pk_val()} - # Prepare the manager. - # TODO: Fix this hack? - # We're setting manager.model here because manager._prepare() expects - # that manager.model is set. This is slightly hackish. manager.model = self.related.model - manager._prepare() return manager @@ -194,13 +189,7 @@ class ReverseManyRelatedObjectsDescriptor(object): add.alters_data = True manager = RelatedManager() - - # Prepare the manager. - # TODO: Fix this hack? - # We're setting manager.model here because manager._prepare() expects - # that manager.model is set. This is slightly hackish. manager.model = self.rel_model - manager._prepare() return manager diff --git a/django/db/models/manager.py b/django/db/models/manager.py index c4f398821a..0d68035baf 100644 --- a/django/db/models/manager.py +++ b/django/db/models/manager.py @@ -16,7 +16,6 @@ def ensure_default_manager(sender): if hasattr(cls, 'objects'): raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % name cls.add_to_class('objects', Manager()) - cls.objects._prepare() dispatcher.connect(ensure_default_manager, signal=signals.class_prepared) @@ -31,23 +30,13 @@ class Manager(object): Manager.creation_counter += 1 self.model = None - def _prepare(self): - if self.model._meta.get_latest_by: - self.get_latest = self.__get_latest - def contribute_to_class(self, model, name): # TODO: Use weakref because of possible memory leak / circular reference. self.model = model - dispatcher.connect(self._prepare, signal=signals.class_prepared, sender=model) setattr(model, name, ManagerDescriptor(self)) if not hasattr(model, '_default_manager') or self.creation_counter < model._default_manager.creation_counter: model._default_manager = self - def __get_latest(self, *args, **kwargs): - kwargs['order_by'] = ('-' + self.model._meta.get_latest_by,) - kwargs['limit'] = 1 - return self.get_object(*args, **kwargs) - ####################### # PROXIES TO QUERYSET # ####################### @@ -89,6 +78,9 @@ class Manager(object): def iterator(self, *args, **kwargs): return self.get_query_set().iterator(*args, **kwargs) + def latest(self, *args, **kwargs): + return self.get_query_set().latest(*args, **kwargs) + def order_by(self, *args, **kwargs): return self.get_query_set().order_by(*args, **kwargs) diff --git a/django/db/models/query.py b/django/db/models/query.py index df748b0a45..c5e0f819c4 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -162,6 +162,15 @@ class QuerySet(object): assert len(obj_list) == 1, "get() returned more than one %s -- it returned %s! Lookup parameters were %s" % (self.model._meta.object_name, len(obj_list), kwargs) return obj_list[0] + def latest(self, field_name=None): + """ + Returns the latest object, according to the model's 'get_latest_by' + option or optional given field_name. + """ + latest_by = field_name or self.model._meta.get_latest_by + assert bool(latest_by), "latest() requires either a field_name parameter or 'get_latest_by' in the model" + return self._clone(_limit=1, _order_by=('-'+latest_by,)).get() + def delete(self, *args, **kwargs): """ Deletes the records with the given kwargs. If no kwargs are given, diff --git a/tests/modeltests/get_latest/models.py b/tests/modeltests/get_latest/models.py index 810ba9bbf7..4b81dc837c 100644 --- a/tests/modeltests/get_latest/models.py +++ b/tests/modeltests/get_latest/models.py @@ -12,32 +12,68 @@ from django.db import models class Article(models.Model): headline = models.CharField(maxlength=100) - pub_date = models.DateTimeField() + pub_date = models.DateField() + expire_date = models.DateField() class Meta: get_latest_by = 'pub_date' def __repr__(self): return self.headline +class Person(models.Model): + name = models.CharField(maxlength=30) + birthday = models.DateField() + + # Note that this model doesn't have "get_latest_by" set. + + def __repr__(self): + return self.name + API_TESTS = """ # Because no Articles exist yet, get_latest() raises ArticleDoesNotExist. ->>> Article.objects.get_latest() +>>> Article.objects.latest() Traceback (most recent call last): ... -DoesNotExist: Article does not exist for {'order_by': ('-pub_date',), 'limit': 1} +DoesNotExist: Article does not exist for ... # Create a couple of Articles. >>> from datetime import datetime ->>> a1 = Article(id=None, headline='Article 1', pub_date=datetime(2005, 7, 26)) +>>> a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26), expire_date=datetime(2005, 9, 1)) >>> a1.save() ->>> a2 = Article(id=None, headline='Article 2', pub_date=datetime(2005, 7, 27)) +>>> a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27), expire_date=datetime(2005, 7, 28)) >>> a2.save() ->>> a3 = Article(id=None, headline='Article 3', pub_date=datetime(2005, 7, 27)) +>>> a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27), expire_date=datetime(2005, 8, 27)) >>> a3.save() ->>> a4 = Article(id=None, headline='Article 4', pub_date=datetime(2005, 7, 28)) +>>> a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28), expire_date=datetime(2005, 7, 30)) >>> a4.save() # Get the latest Article. ->>> Article.objects.get_latest() +>>> Article.objects.latest() Article 4 + +# Get the latest Article that matches certain filters. +>>> Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest() +Article 1 + +# Pass a custom field name to latest() to change the field that's used to +# determine the latest object. +>>> Article.objects.latest('expire_date') +Article 1 + +>>> Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest('expire_date') +Article 3 + +# You can still use latest() with a model that doesn't have "get_latest_by" +# set -- just pass in the field name manually. +>>> p1 = Person(name='Ralph', birthday=datetime(1950, 1, 1)) +>>> p1.save() +>>> p2 = Person(name='Stephanie', birthday=datetime(1960, 2, 3)) +>>> p2.save() +>>> Person.objects.latest() +Traceback (most recent call last): + ... +AssertionError: latest() requires either a field_name parameter or 'get_latest_by' in the model + +>>> Person.objects.latest('birthday') +Stephanie """