Fixed #25470 -- Avoided unnecessary, expensive DATETIME typecast on MySQL.
This commit is contained in:
parent
0dbe897ab5
commit
0f6d51e6a0
|
@ -27,17 +27,15 @@ class DatabaseOperations(BaseDatabaseOperations):
|
||||||
return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
|
return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
|
||||||
|
|
||||||
def date_trunc_sql(self, lookup_type, field_name):
|
def date_trunc_sql(self, lookup_type, field_name):
|
||||||
fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
|
fields = {
|
||||||
format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s') # Use double percents to escape.
|
'year': '%%Y-01-01',
|
||||||
format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
|
'month': '%%Y-%%m-01',
|
||||||
try:
|
} # Use double percents to escape.
|
||||||
i = fields.index(lookup_type) + 1
|
if lookup_type in fields:
|
||||||
except ValueError:
|
format_str = fields[lookup_type]
|
||||||
sql = field_name
|
return "CAST(DATE_FORMAT(%s, '%s') AS DATE)" % (field_name, format_str)
|
||||||
else:
|
else:
|
||||||
format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
|
return "DATE(%s)" % (field_name)
|
||||||
sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
|
|
||||||
return sql
|
|
||||||
|
|
||||||
def _convert_field_to_tz(self, field_name, tzname):
|
def _convert_field_to_tz(self, field_name, tzname):
|
||||||
if settings.USE_TZ:
|
if settings.USE_TZ:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
from django.utils.encoding import python_2_unicode_compatible
|
from django.utils.encoding import python_2_unicode_compatible
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,6 +9,7 @@ from django.utils.encoding import python_2_unicode_compatible
|
||||||
class Article(models.Model):
|
class Article(models.Model):
|
||||||
title = models.CharField(max_length=100)
|
title = models.CharField(max_length=100)
|
||||||
pub_date = models.DateField()
|
pub_date = models.DateField()
|
||||||
|
pub_datetime = models.DateTimeField(default=timezone.now())
|
||||||
|
|
||||||
categories = models.ManyToManyField("Category", related_name="articles")
|
categories = models.ManyToManyField("Category", related_name="articles")
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
from unittest import skipUnless
|
||||||
|
|
||||||
from django.core.exceptions import FieldError
|
from django.core.exceptions import FieldError
|
||||||
from django.test import TestCase
|
from django.db import connection
|
||||||
|
from django.test import TestCase, override_settings
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
|
||||||
from .models import Article, Category, Comment
|
from .models import Article, Category, Comment
|
||||||
|
@ -95,7 +97,7 @@ class DatesTests(TestCase):
|
||||||
self,
|
self,
|
||||||
FieldError,
|
FieldError,
|
||||||
"Cannot resolve keyword u?'invalid_field' into field. Choices are: "
|
"Cannot resolve keyword u?'invalid_field' into field. Choices are: "
|
||||||
"categories, comments, id, pub_date, title",
|
"categories, comments, id, pub_date, pub_datetime, title",
|
||||||
Article.objects.dates,
|
Article.objects.dates,
|
||||||
"invalid_field",
|
"invalid_field",
|
||||||
"year",
|
"year",
|
||||||
|
@ -121,3 +123,31 @@ class DatesTests(TestCase):
|
||||||
"year",
|
"year",
|
||||||
order="bad order",
|
order="bad order",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@override_settings(USE_TZ=False)
|
||||||
|
def test_dates_trunc_datetime_fields(self):
|
||||||
|
Article.objects.bulk_create(
|
||||||
|
Article(pub_date=pub_datetime.date(), pub_datetime=pub_datetime)
|
||||||
|
for pub_datetime in [
|
||||||
|
datetime.datetime(2015, 10, 21, 18, 1),
|
||||||
|
datetime.datetime(2015, 10, 21, 18, 2),
|
||||||
|
datetime.datetime(2015, 10, 22, 18, 1),
|
||||||
|
datetime.datetime(2015, 10, 22, 18, 2),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertQuerysetEqual(
|
||||||
|
Article.objects.dates('pub_datetime', 'day', order='ASC'), [
|
||||||
|
"datetime.date(2015, 10, 21)",
|
||||||
|
"datetime.date(2015, 10, 22)",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@skipUnless(connection.vendor == 'mysql', "Test checks MySQL query syntax")
|
||||||
|
def test_dates_avoid_datetime_cast(self):
|
||||||
|
Article.objects.create(pub_date=datetime.date(2015, 10, 21))
|
||||||
|
for kind in ['day', 'month', 'year']:
|
||||||
|
qs = Article.objects.dates('pub_date', kind)
|
||||||
|
if kind == 'day':
|
||||||
|
self.assertIn('DATE(', str(qs.query))
|
||||||
|
else:
|
||||||
|
self.assertIn(' AS DATE)', str(qs.query))
|
||||||
|
|
Loading…
Reference in New Issue