summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMariusz Felisiak <felisiak.mariusz@gmail.com>2017-07-17 21:12:27 +0200
committerGitHub <noreply@github.com>2017-07-17 21:12:27 +0200
commit776cee974933677730aef177eb5b5bdec3749c80 (patch)
treee6435cf6aeabe6b436a87ab8fb1ef702608e8a74
parentfeeafdad02e2874e2e2f879a825d3527f6b193ad (diff)
Fixed #28391 -- Fixed Cast() with CharField and max_length on MySQL.
Thanks Tim Graham for the review.
-rw-r--r--django/db/backends/base/features.py3
-rw-r--r--django/db/backends/sqlite3/features.py1
-rw-r--r--django/db/models/fields/__init__.py7
-rw-r--r--django/db/models/functions/base.py5
-rw-r--r--tests/db_functions/test_cast.py9
5 files changed, 20 insertions, 5 deletions
diff --git a/django/db/backends/base/features.py b/django/db/backends/base/features.py
index 41a456bfed..0cc60fd450 100644
--- a/django/db/backends/base/features.py
+++ b/django/db/backends/base/features.py
@@ -234,6 +234,9 @@ class BaseDatabaseFeatures:
# Does the backend support indexing a TextField?
supports_index_on_text_field = True
+ # Does the backend support CAST with precision?
+ supports_cast_with_precision = True
+
def __init__(self, connection):
self.connection = connection
diff --git a/django/db/backends/sqlite3/features.py b/django/db/backends/sqlite3/features.py
index 80af6068ff..c82d565b6e 100644
--- a/django/db/backends/sqlite3/features.py
+++ b/django/db/backends/sqlite3/features.py
@@ -30,6 +30,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
can_clone_databases = True
supports_temporal_subtraction = True
ignores_table_name_case = True
+ supports_cast_with_precision = False
@cached_property
def uses_savepoints(self):
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 260882bf5b..e3a8e85567 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -607,13 +607,16 @@ class Field(RegisterLookupMixin):
self.run_validators(value)
return value
+ def db_type_parameters(self, connection):
+ return DictWrapper(self.__dict__, connection.ops.quote_name, 'qn_')
+
def db_check(self, connection):
"""
Return the database column check constraint for this field, for the
provided connection. Works the same way as db_type() for the case that
get_internal_type() does not map to a preexisting model field.
"""
- data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
+ data = self.db_type_parameters(connection)
try:
return connection.data_type_check_constraints[self.get_internal_type()] % data
except KeyError:
@@ -639,7 +642,7 @@ class Field(RegisterLookupMixin):
# mapped to one of the built-in Django field types. In this case, you
# can implement db_type() instead of get_internal_type() to specify
# exactly which wacky database column type you want to use.
- data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
+ data = self.db_type_parameters(connection)
try:
return connection.data_types[self.get_internal_type()] % data
except KeyError:
diff --git a/django/db/models/functions/base.py b/django/db/models/functions/base.py
index 82a6083c58..a965f4107c 100644
--- a/django/db/models/functions/base.py
+++ b/django/db/models/functions/base.py
@@ -10,7 +10,7 @@ class Cast(Func):
template = '%(function)s(%(expressions)s AS %(db_type)s)'
mysql_types = {
- fields.CharField: 'char',
+ fields.CharField: 'char(%(max_length)s)',
fields.IntegerField: 'signed integer',
fields.BigIntegerField: 'signed integer',
fields.SmallIntegerField: 'signed integer',
@@ -31,7 +31,8 @@ class Cast(Func):
extra_context = {}
output_field_class = type(self.output_field)
if output_field_class in self.mysql_types:
- extra_context['db_type'] = self.mysql_types[output_field_class]
+ data = self.output_field.db_type_parameters(connection)
+ extra_context['db_type'] = self.mysql_types[output_field_class] % data
return self.as_sql(compiler, connection, **extra_context)
def as_postgresql(self, compiler, connection):
diff --git a/tests/db_functions/test_cast.py b/tests/db_functions/test_cast.py
index 70de20e5eb..bd4541bf06 100644
--- a/tests/db_functions/test_cast.py
+++ b/tests/db_functions/test_cast.py
@@ -1,7 +1,7 @@
from django.db import models
from django.db.models.expressions import Value
from django.db.models.functions import Cast
-from django.test import TestCase
+from django.test import TestCase, ignore_warnings, skipUnlessDBFeature
from .models import Author
@@ -19,6 +19,13 @@ class CastTests(TestCase):
numbers = Author.objects.annotate(cast_string=Cast('age', models.CharField(max_length=255)),)
self.assertEqual(numbers.get().cast_string, '1')
+ # Silence "Truncated incorrect CHAR(1) value: 'Bob'".
+ @ignore_warnings(module='django.db.backends.mysql.base')
+ @skipUnlessDBFeature('supports_cast_with_precision')
+ def test_cast_to_char_field_with_max_length(self):
+ names = Author.objects.annotate(cast_string=Cast('name', models.CharField(max_length=1)))
+ self.assertEqual(names.get().cast_string, 'B')
+
def test_cast_to_integer(self):
for field_class in (
models.IntegerField,