Skip to content

Commit

Permalink
Changed __year lookup to use a BETWEEN SQL statement instead of compa…
Browse files Browse the repository at this point in the history
…ring the result of EXTRACT(year). This should be more efficient.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@4505 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
adrianholovaty committed Feb 14, 2007
1 parent 9efa60d commit 4c4209b
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 17 deletions.
10 changes: 8 additions & 2 deletions django/db/models/fields/__init__.py
Expand Up @@ -164,7 +164,7 @@ def get_db_prep_save(self, value):


def get_db_prep_lookup(self, lookup_type, value): def get_db_prep_lookup(self, lookup_type, value):
"Returns field's value prepared for database lookup." "Returns field's value prepared for database lookup."
if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'year', 'month', 'day', 'search'): if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):
return [value] return [value]
elif lookup_type in ('range', 'in'): elif lookup_type in ('range', 'in'):
return value return value
Expand All @@ -178,7 +178,13 @@ def get_db_prep_lookup(self, lookup_type, value):
return ["%%%s" % prep_for_like_query(value)] return ["%%%s" % prep_for_like_query(value)]
elif lookup_type == 'isnull': elif lookup_type == 'isnull':
return [] return []
raise TypeError, "Field has invalid lookup: %s" % lookup_type elif lookup_type == 'year':
try:
value = int(value)
except ValueError:
raise ValueError("The __year lookup type requires an integer argument")
return ['%s-01-01 00:00:00' % value, '%s-12-31 23:59:59.999999' % value]
raise TypeError("Field has invalid lookup: %s" % lookup_type)


def has_default(self): def has_default(self):
"Returns a boolean of whether this field has a default value." "Returns a boolean of whether this field has a default value."
Expand Down
26 changes: 13 additions & 13 deletions django/db/models/query.py
Expand Up @@ -198,17 +198,17 @@ def count(self):
counter = self._clone() counter = self._clone()
counter._order_by = () counter._order_by = ()
counter._select_related = False counter._select_related = False

offset = counter._offset offset = counter._offset
limit = counter._limit limit = counter._limit
counter._offset = None counter._offset = None
counter._limit = None counter._limit = None

try: try:
select, sql, params = counter._get_sql_clause() select, sql, params = counter._get_sql_clause()
except EmptyResultSet: except EmptyResultSet:
return 0 return 0

cursor = connection.cursor() cursor = connection.cursor()
if self._distinct: if self._distinct:
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table), id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
Expand Down Expand Up @@ -553,7 +553,7 @@ def iterator(self):
else: # Default to all fields. else: # Default to all fields.
columns = [f.column for f in self.model._meta.fields] columns = [f.column for f in self.model._meta.fields]
field_names = [f.attname for f in self.model._meta.fields] field_names = [f.attname for f in self.model._meta.fields]

select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns] select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table), backend.quote_name(c)) for c in columns]
cursor = connection.cursor() cursor = connection.cursor()
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
Expand All @@ -576,12 +576,12 @@ def iterator(self):
if self._field.null: if self._field.null:
self._where.append('%s.%s IS NOT NULL' % \ self._where.append('%s.%s IS NOT NULL' % \
(backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column))) (backend.quote_name(self.model._meta.db_table), backend.quote_name(self._field.column)))

try: try:
select, sql, params = self._get_sql_clause() select, sql, params = self._get_sql_clause()
except EmptyResultSet: except EmptyResultSet:
raise StopIteration raise StopIteration

sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \ sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \
(backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table), (backend.get_date_trunc_sql(self._kind, '%s.%s' % (backend.quote_name(self.model._meta.db_table),
backend.quote_name(self._field.column))), sql, self._order) backend.quote_name(self._field.column))), sql, self._order)
Expand All @@ -598,15 +598,15 @@ def _clone(self, klass=None, **kwargs):
c._kind = self._kind c._kind = self._kind
c._order = self._order c._order = self._order
return c return c

class EmptyQuerySet(QuerySet): class EmptyQuerySet(QuerySet):
def __init__(self, model=None): def __init__(self, model=None):
super(EmptyQuerySet, self).__init__(model) super(EmptyQuerySet, self).__init__(model)
self._result_cache = [] self._result_cache = []

def count(self): def count(self):
return 0 return 0

def delete(self): def delete(self):
pass pass


Expand Down Expand Up @@ -708,9 +708,9 @@ def get_where_clause(lookup_type, table_prefix, field_name, value):
return '%s%s IN (%s)' % (table_prefix, field_name, in_string) return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
else: else:
raise EmptyResultSet raise EmptyResultSet
elif lookup_type == 'range': elif lookup_type in ('range', 'year'):
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name) return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
elif lookup_type in ('year', 'month', 'day'): elif lookup_type in ('month', 'day'):
return "%s = %%s" % backend.get_date_extract_sql(lookup_type, table_prefix + field_name) return "%s = %%s" % backend.get_date_extract_sql(lookup_type, table_prefix + field_name)
elif lookup_type == 'isnull': elif lookup_type == 'isnull':
return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or '')) return "%s%s IS %sNULL" % (table_prefix, field_name, (not value and 'NOT ' or ''))
Expand Down Expand Up @@ -791,7 +791,7 @@ def parse_lookup(kwarg_items, opts):


if len(path) < 1: if len(path) < 1:
raise TypeError, "Cannot parse keyword query %r" % kwarg raise TypeError, "Cannot parse keyword query %r" % kwarg

if value is None: if value is None:
# Interpret '__exact=None' as the sql '= NULL'; otherwise, reject # Interpret '__exact=None' as the sql '= NULL'; otherwise, reject
# all uses of None as a query value. # all uses of None as a query value.
Expand Down Expand Up @@ -1007,7 +1007,7 @@ def delete_objects(seen_objs):
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]) pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
for f in cls._meta.many_to_many: for f in cls._meta.many_to_many:
if isinstance(f, GenericRelation): if isinstance(f, GenericRelation):
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
args_extra = [ContentType.objects.get_for_model(cls).id] args_extra = [ContentType.objects.get_for_model(cls).id]
else: else:
Expand Down
10 changes: 8 additions & 2 deletions tests/modeltests/basic/models.py
Expand Up @@ -12,7 +12,7 @@ class Article(models.Model):


class Meta: class Meta:
ordering = ('pub_date','headline') ordering = ('pub_date','headline')

def __str__(self): def __str__(self):
return self.headline return self.headline


Expand Down Expand Up @@ -319,7 +319,6 @@ def __str__(self):
>>> Article.objects.filter(id__lte=4).delete() >>> Article.objects.filter(id__lte=4).delete()
>>> Article.objects.all() >>> Article.objects.all()
[<Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>] [<Article: Article 6>, <Article: Default headline>, <Article: Article 7>, <Article: Updated article 8>]
"""} """}


from django.conf import settings from django.conf import settings
Expand Down Expand Up @@ -358,4 +357,11 @@ def __str__(self):
>>> a10 = Article.objects.create(headline="Article 10", pub_date=datetime(2005, 7, 31, 12, 30, 45)) >>> a10 = Article.objects.create(headline="Article 10", pub_date=datetime(2005, 7, 31, 12, 30, 45))
>>> Article.objects.get(headline="Article 10") >>> Article.objects.get(headline="Article 10")
<Article: Article 10> <Article: Article 10>
# Edge-case test: A year lookup should retrieve all objects in the given
year, including Jan. 1 and Dec. 31.
>>> a11 = Article.objects.create(headline='Article 11', pub_date=datetime(2008, 1, 1))
>>> a12 = Article.objects.create(headline='Article 12', pub_date=datetime(2008, 12, 31, 23, 59, 59, 999999))
>>> Article.objects.filter(pub_date__year=2008)
[<Article: Article 11>, <Article: Article 12>]
""" """

0 comments on commit 4c4209b

Please sign in to comment.