From ef9f109013adc6e140c1a71d874aa88e893657b7 Mon Sep 17 00:00:00 2001 From: Marc Tamlyn Date: Tue, 15 Jul 2014 12:30:34 +0100 Subject: [PATCH] Fixed #22962 -- Default values for ArrayField with migrations. Fields normally try to force the default value to a string. As translatable strings are not valid default values for ArrayField, we can remove this behaviour which was causing issues with some migrations. Thanks to @schinckel for the report. --- django/contrib/postgres/fields/array.py | 8 +++ .../array_default_migrations/0001_initial.py | 64 +++++++++++++++++++ .../0002_integerarraymodel_field_2.py | 21 ++++++ .../array_default_migrations/__init__.py | 0 tests/postgres_tests/test_array.py | 10 ++- 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 tests/postgres_tests/array_default_migrations/0001_initial.py create mode 100644 tests/postgres_tests/array_default_migrations/0002_integerarraymodel_field_2.py create mode 100644 tests/postgres_tests/array_default_migrations/__init__.py diff --git a/django/contrib/postgres/fields/array.py b/django/contrib/postgres/fields/array.py index 772ab9e586..dea9c0440d 100644 --- a/django/contrib/postgres/fields/array.py +++ b/django/contrib/postgres/fields/array.py @@ -94,6 +94,14 @@ class ArrayField(Field): value = [self.base_field.to_python(val) for val in vals] return value + def get_default(self): + """Overridden from the default to prevent string-mangling.""" + if self.has_default(): + if callable(self.default): + return self.default() + return self.default + return '' + def value_to_string(self, obj): values = [] vals = self._get_val_from_obj(obj) diff --git a/tests/postgres_tests/array_default_migrations/0001_initial.py b/tests/postgres_tests/array_default_migrations/0001_initial.py new file mode 100644 index 0000000000..0b7eb77c3d --- /dev/null +++ b/tests/postgres_tests/array_default_migrations/0001_initial.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.contrib.postgres.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='CharArrayModel', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('field', django.contrib.postgres.fields.ArrayField(models.CharField(max_length=10), size=None)), + ], + options={ + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='DateTimeArrayModel', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('field', django.contrib.postgres.fields.ArrayField(models.DateTimeField(), size=None)), + ], + options={ + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='IntegerArrayModel', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('field', django.contrib.postgres.fields.ArrayField(models.IntegerField(), size=None)), + ], + options={ + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='NestedIntegerArrayModel', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('field', django.contrib.postgres.fields.ArrayField(django.contrib.postgres.fields.ArrayField(models.IntegerField(), size=None), size=None)), + ], + options={ + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='NullableIntegerArrayModel', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('field', django.contrib.postgres.fields.ArrayField(models.IntegerField(), size=None, null=True, blank=True)), + ], + options={ + }, + bases=(models.Model,), + ), + ] diff --git a/tests/postgres_tests/array_default_migrations/0002_integerarraymodel_field_2.py b/tests/postgres_tests/array_default_migrations/0002_integerarraymodel_field_2.py new file mode 100644 index 0000000000..074c2f70e5 --- /dev/null +++ b/tests/postgres_tests/array_default_migrations/0002_integerarraymodel_field_2.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.contrib.postgres.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('postgres_tests', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='integerarraymodel', + name='field_2', + field=django.contrib.postgres.fields.ArrayField(models.IntegerField(), default=[], size=None), + preserve_default=False, + ), + ] diff --git a/tests/postgres_tests/array_default_migrations/__init__.py b/tests/postgres_tests/array_default_migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/postgres_tests/test_array.py b/tests/postgres_tests/test_array.py index 1fc54defe7..33cf837dd4 100644 --- a/tests/postgres_tests/test_array.py +++ b/tests/postgres_tests/test_array.py @@ -4,10 +4,11 @@ import unittest from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.forms import SimpleArrayField, SplitArrayField from django.core import exceptions, serializers +from django.core.management import call_command from django.db import models, IntegrityError, connection from django.db.migrations.writer import MigrationWriter from django import forms -from django.test import TestCase +from django.test import TestCase, override_settings from django.utils import timezone from .models import IntegerArrayModel, NullableIntegerArrayModel, CharArrayModel, DateTimeArrayModel, NestedIntegerArrayModel @@ -226,6 +227,13 @@ class TestMigrations(TestCase): statement, imports = MigrationWriter.serialize(field) self.assertEqual(statement, 'django.contrib.postgres.fields.ArrayField(models.CharField(max_length=20), size=None)') + @override_settings(MIGRATION_MODULES={ + "postgres_tests": "postgres_tests.array_default_migrations", + }) + def test_adding_field_with_default(self): + # See #22962 + call_command('migrate', 'postgres_tests', verbosity=0) + @unittest.skipUnless(connection.vendor == 'postgresql', 'PostgreSQL required') class TestSerialization(TestCase):