Просмотр исходного кода

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.
Marc Tamlyn 10 лет назад
Родитель
Сommit
ef9f109013

+ 8 - 0
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)

+ 64 - 0
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,),
+        ),
+    ]

+ 21 - 0
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,
+        ),
+    ]

+ 0 - 0
tests/postgres_tests/array_default_migrations/__init__.py


+ 9 - 1
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):