Prechádzať zdrojové kódy

Fixed #25470 -- Avoided unnecessary, expensive DATETIME typecast on MySQL.

Mariusz Felisiak 9 rokov pred
rodič
commit
0f6d51e6a0

+ 8 - 10
django/db/backends/mysql/operations.py

@@ -27,17 +27,15 @@ class DatabaseOperations(BaseDatabaseOperations):
             return "EXTRACT(%s FROM %s)" % (lookup_type.upper(), field_name)
 
     def date_trunc_sql(self, lookup_type, field_name):
-        fields = ['year', 'month', 'day', 'hour', 'minute', 'second']
-        format = ('%%Y-', '%%m', '-%%d', ' %%H:', '%%i', ':%%s')  # Use double percents to escape.
-        format_def = ('0000-', '01', '-01', ' 00:', '00', ':00')
-        try:
-            i = fields.index(lookup_type) + 1
-        except ValueError:
-            sql = field_name
+        fields = {
+            'year': '%%Y-01-01',
+            'month': '%%Y-%%m-01',
+        }  # Use double percents to escape.
+        if lookup_type in fields:
+            format_str = fields[lookup_type]
+            return "CAST(DATE_FORMAT(%s, '%s') AS DATE)" % (field_name, format_str)
         else:
-            format_str = ''.join([f for f in format[:i]] + [f for f in format_def[i:]])
-            sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
-        return sql
+            return "DATE(%s)" % (field_name)
 
     def _convert_field_to_tz(self, field_name, tzname):
         if settings.USE_TZ:

+ 2 - 0
tests/dates/models.py

@@ -1,6 +1,7 @@
 from __future__ import unicode_literals
 
 from django.db import models
+from django.utils import timezone
 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):
     title = models.CharField(max_length=100)
     pub_date = models.DateField()
+    pub_datetime = models.DateTimeField(default=timezone.now())
 
     categories = models.ManyToManyField("Category", related_name="articles")
 

+ 32 - 2
tests/dates/tests.py

@@ -1,9 +1,11 @@
 from __future__ import unicode_literals
 
 import datetime
+from unittest import skipUnless
 
 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 .models import Article, Category, Comment
@@ -95,7 +97,7 @@ class DatesTests(TestCase):
             self,
             FieldError,
             "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,
             "invalid_field",
             "year",
@@ -121,3 +123,31 @@ class DatesTests(TestCase):
             "year",
             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))