From b61d5b1991e2ca2c3450ccc334224f3d51da39dc Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 27 Jul 2017 19:36:47 +0200 Subject: [PATCH] Fixed #28371 -- Fixed Cast() with CharField if the max_length argument isn't provided. Thanks Tim Graham for the review. --- django/db/backends/base/operations.py | 2 ++ django/db/backends/mysql/operations.py | 1 + django/db/backends/oracle/operations.py | 3 +++ django/db/backends/postgresql/operations.py | 2 ++ django/db/backends/sqlite3/operations.py | 2 ++ django/db/models/fields/__init__.py | 5 +++++ docs/releases/2.0.txt | 6 ++++++ tests/db_functions/test_cast.py | 4 ++++ 8 files changed, 25 insertions(+) diff --git a/django/db/backends/base/operations.py b/django/db/backends/base/operations.py index effdffd6e7..495d7e910b 100644 --- a/django/db/backends/base/operations.py +++ b/django/db/backends/base/operations.py @@ -36,6 +36,8 @@ class BaseDatabaseOperations: # name) to the data type to use for the Cast() function, if different from # DatabaseWrapper.data_types. cast_data_types = {} + # CharField data type if the max_length argument isn't provided. + cast_char_field_without_max_length = None def __init__(self, connection): self.connection = connection diff --git a/django/db/backends/mysql/operations.py b/django/db/backends/mysql/operations.py index 71592234aa..e4492f866f 100644 --- a/django/db/backends/mysql/operations.py +++ b/django/db/backends/mysql/operations.py @@ -24,6 +24,7 @@ class DatabaseOperations(BaseDatabaseOperations): 'PositiveIntegerField': 'unsigned integer', 'PositiveSmallIntegerField': 'unsigned integer', } + cast_char_field_without_max_length = 'char' def date_extract_sql(self, lookup_type, field_name): # http://dev.mysql.com/doc/mysql/en/date-and-time-functions.html diff --git a/django/db/backends/oracle/operations.py b/django/db/backends/oracle/operations.py index 903bad3307..60fce427d7 100644 --- a/django/db/backends/oracle/operations.py +++ b/django/db/backends/oracle/operations.py @@ -49,6 +49,9 @@ BEGIN END; /""" + # Oracle doesn't support string without precision; use the max string size. + cast_char_field_without_max_length = 'NVARCHAR2(2000)' + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.set_operators['difference'] = 'MINUS' diff --git a/django/db/backends/postgresql/operations.py b/django/db/backends/postgresql/operations.py index 497a5303a2..2cc886a76d 100644 --- a/django/db/backends/postgresql/operations.py +++ b/django/db/backends/postgresql/operations.py @@ -5,6 +5,8 @@ from django.db.backends.base.operations import BaseDatabaseOperations class DatabaseOperations(BaseDatabaseOperations): + cast_char_field_without_max_length = 'varchar' + def unification_cast_sql(self, output_field): internal_type = output_field.get_internal_type() if internal_type in ("GenericIPAddressField", "IPAddressField", "TimeField", "UUIDField"): diff --git a/django/db/backends/sqlite3/operations.py b/django/db/backends/sqlite3/operations.py index f3c31ee1b9..38f7ca7b69 100644 --- a/django/db/backends/sqlite3/operations.py +++ b/django/db/backends/sqlite3/operations.py @@ -14,6 +14,8 @@ from django.utils.duration import duration_string class DatabaseOperations(BaseDatabaseOperations): + cast_char_field_without_max_length = 'text' + def bulk_batch_size(self, fields, objs): """ SQLite has a compile-time default (SQLITE_LIMIT_VARIABLE_NUMBER) of diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 5963339dad..1cfd0294e1 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -1066,6 +1066,11 @@ class CharField(Field): else: return [] + def cast_db_type(self, connection): + if self.max_length is None: + return connection.ops.cast_char_field_without_max_length + return super().cast_db_type(connection) + def get_internal_type(self): return "CharField" diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt index 36359e4979..87f95210ee 100644 --- a/docs/releases/2.0.txt +++ b/docs/releases/2.0.txt @@ -348,6 +348,12 @@ backends. requires that the arguments to ``OF`` be columns rather than tables, set ``DatabaseFeatures.select_for_update_of_column = True``. +* Third-party database backends should add a + ``DatabaseOperations.cast_char_field_without_max_length`` attribute with the + database data type that will be used in the + :class:`~django.db.models.functions.Cast` function for a ``CharField`` if the + ``max_length`` argument isn't provided. + Dropped support for Oracle 11.2 ------------------------------- diff --git a/tests/db_functions/test_cast.py b/tests/db_functions/test_cast.py index bd4541bf06..e266a13db1 100644 --- a/tests/db_functions/test_cast.py +++ b/tests/db_functions/test_cast.py @@ -19,6 +19,10 @@ class CastTests(TestCase): numbers = Author.objects.annotate(cast_string=Cast('age', models.CharField(max_length=255)),) self.assertEqual(numbers.get().cast_string, '1') + def test_cast_to_char_field_without_max_length(self): + numbers = Author.objects.annotate(cast_string=Cast('age', models.CharField())) + 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')