Fixed #32573 -- Fixed bounds in __iso_year lookup optimization.
This commit is contained in:
parent
6efc35b4fe
commit
3a185cee2a
|
@ -526,28 +526,44 @@ class BaseDatabaseOperations:
|
||||||
"""
|
"""
|
||||||
return value or None
|
return value or None
|
||||||
|
|
||||||
def year_lookup_bounds_for_date_field(self, value):
|
def year_lookup_bounds_for_date_field(self, value, iso_year=False):
|
||||||
"""
|
"""
|
||||||
Return a two-elements list with the lower and upper bound to be used
|
Return a two-elements list with the lower and upper bound to be used
|
||||||
with a BETWEEN operator to query a DateField value using a year
|
with a BETWEEN operator to query a DateField value using a year
|
||||||
lookup.
|
lookup.
|
||||||
|
|
||||||
`value` is an int, containing the looked-up year.
|
`value` is an int, containing the looked-up year.
|
||||||
|
If `iso_year` is True, return bounds for ISO-8601 week-numbering years.
|
||||||
"""
|
"""
|
||||||
|
if iso_year:
|
||||||
|
first = datetime.date.fromisocalendar(value, 1, 1)
|
||||||
|
second = (
|
||||||
|
datetime.date.fromisocalendar(value + 1, 1, 1) -
|
||||||
|
datetime.timedelta(days=1)
|
||||||
|
)
|
||||||
|
else:
|
||||||
first = datetime.date(value, 1, 1)
|
first = datetime.date(value, 1, 1)
|
||||||
second = datetime.date(value, 12, 31)
|
second = datetime.date(value, 12, 31)
|
||||||
first = self.adapt_datefield_value(first)
|
first = self.adapt_datefield_value(first)
|
||||||
second = self.adapt_datefield_value(second)
|
second = self.adapt_datefield_value(second)
|
||||||
return [first, second]
|
return [first, second]
|
||||||
|
|
||||||
def year_lookup_bounds_for_datetime_field(self, value):
|
def year_lookup_bounds_for_datetime_field(self, value, iso_year=False):
|
||||||
"""
|
"""
|
||||||
Return a two-elements list with the lower and upper bound to be used
|
Return a two-elements list with the lower and upper bound to be used
|
||||||
with a BETWEEN operator to query a DateTimeField value using a year
|
with a BETWEEN operator to query a DateTimeField value using a year
|
||||||
lookup.
|
lookup.
|
||||||
|
|
||||||
`value` is an int, containing the looked-up year.
|
`value` is an int, containing the looked-up year.
|
||||||
|
If `iso_year` is True, return bounds for ISO-8601 week-numbering years.
|
||||||
"""
|
"""
|
||||||
|
if iso_year:
|
||||||
|
first = datetime.datetime.fromisocalendar(value, 1, 1)
|
||||||
|
second = (
|
||||||
|
datetime.datetime.fromisocalendar(value + 1, 1, 1) -
|
||||||
|
datetime.timedelta(microseconds=1)
|
||||||
|
)
|
||||||
|
else:
|
||||||
first = datetime.datetime(value, 1, 1)
|
first = datetime.datetime(value, 1, 1)
|
||||||
second = datetime.datetime(value, 12, 31, 23, 59, 59, 999999)
|
second = datetime.datetime(value, 12, 31, 23, 59, 59, 999999)
|
||||||
if settings.USE_TZ:
|
if settings.USE_TZ:
|
||||||
|
|
|
@ -539,11 +539,17 @@ class IRegex(Regex):
|
||||||
|
|
||||||
class YearLookup(Lookup):
|
class YearLookup(Lookup):
|
||||||
def year_lookup_bounds(self, connection, year):
|
def year_lookup_bounds(self, connection, year):
|
||||||
|
from django.db.models.functions import ExtractIsoYear
|
||||||
|
iso_year = isinstance(self.lhs, ExtractIsoYear)
|
||||||
output_field = self.lhs.lhs.output_field
|
output_field = self.lhs.lhs.output_field
|
||||||
if isinstance(output_field, DateTimeField):
|
if isinstance(output_field, DateTimeField):
|
||||||
bounds = connection.ops.year_lookup_bounds_for_datetime_field(year)
|
bounds = connection.ops.year_lookup_bounds_for_datetime_field(
|
||||||
|
year, iso_year=iso_year,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
bounds = connection.ops.year_lookup_bounds_for_date_field(year)
|
bounds = connection.ops.year_lookup_bounds_for_date_field(
|
||||||
|
year, iso_year=iso_year,
|
||||||
|
)
|
||||||
return bounds
|
return bounds
|
||||||
|
|
||||||
def as_sql(self, compiler, connection):
|
def as_sql(self, compiler, connection):
|
||||||
|
|
|
@ -289,7 +289,10 @@ Database backend API
|
||||||
This section describes changes that may be needed in third-party database
|
This section describes changes that may be needed in third-party database
|
||||||
backends.
|
backends.
|
||||||
|
|
||||||
* ...
|
* ``DatabaseOperations.year_lookup_bounds_for_date_field()`` and
|
||||||
|
``year_lookup_bounds_for_datetime_field()`` methods now take the optional
|
||||||
|
``iso_year`` argument in order to support bounds for ISO-8601 week-numbering
|
||||||
|
years.
|
||||||
|
|
||||||
:mod:`django.contrib.gis`
|
:mod:`django.contrib.gis`
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
|
@ -359,9 +359,9 @@ class DateFunctionTests(TestCase):
|
||||||
week_52_day_2014 = timezone.make_aware(week_52_day_2014, is_dst=False)
|
week_52_day_2014 = timezone.make_aware(week_52_day_2014, is_dst=False)
|
||||||
week_53_day_2015 = timezone.make_aware(week_53_day_2015, is_dst=False)
|
week_53_day_2015 = timezone.make_aware(week_53_day_2015, is_dst=False)
|
||||||
days = [week_52_day_2014, week_1_day_2014_2015, week_53_day_2015]
|
days = [week_52_day_2014, week_1_day_2014_2015, week_53_day_2015]
|
||||||
self.create_model(week_53_day_2015, end_datetime)
|
obj_1_iso_2014 = self.create_model(week_52_day_2014, end_datetime)
|
||||||
self.create_model(week_52_day_2014, end_datetime)
|
obj_1_iso_2015 = self.create_model(week_1_day_2014_2015, end_datetime)
|
||||||
self.create_model(week_1_day_2014_2015, end_datetime)
|
obj_2_iso_2015 = self.create_model(week_53_day_2015, end_datetime)
|
||||||
qs = DTModel.objects.filter(start_datetime__in=days).annotate(
|
qs = DTModel.objects.filter(start_datetime__in=days).annotate(
|
||||||
extracted=ExtractIsoYear('start_datetime'),
|
extracted=ExtractIsoYear('start_datetime'),
|
||||||
).order_by('start_datetime')
|
).order_by('start_datetime')
|
||||||
|
@ -371,6 +371,19 @@ class DateFunctionTests(TestCase):
|
||||||
(week_53_day_2015, 2015),
|
(week_53_day_2015, 2015),
|
||||||
], lambda m: (m.start_datetime, m.extracted))
|
], lambda m: (m.start_datetime, m.extracted))
|
||||||
|
|
||||||
|
qs = DTModel.objects.filter(
|
||||||
|
start_datetime__iso_year=2015,
|
||||||
|
).order_by('start_datetime')
|
||||||
|
self.assertSequenceEqual(qs, [obj_1_iso_2015, obj_2_iso_2015])
|
||||||
|
qs = DTModel.objects.filter(
|
||||||
|
start_datetime__iso_year__gt=2014,
|
||||||
|
).order_by('start_datetime')
|
||||||
|
self.assertSequenceEqual(qs, [obj_1_iso_2015, obj_2_iso_2015])
|
||||||
|
qs = DTModel.objects.filter(
|
||||||
|
start_datetime__iso_year__lte=2014,
|
||||||
|
).order_by('start_datetime')
|
||||||
|
self.assertSequenceEqual(qs, [obj_1_iso_2014])
|
||||||
|
|
||||||
def test_extract_month_func(self):
|
def test_extract_month_func(self):
|
||||||
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
|
start_datetime = datetime(2015, 6, 15, 14, 30, 50, 321)
|
||||||
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)
|
end_datetime = datetime(2016, 6, 15, 14, 10, 50, 123)
|
||||||
|
|
Loading…
Reference in New Issue