Example #1
0
 def test_simple_migration(self):
     """
     Tests serializing a simple migration.
     """
     migration = type(
         str("Migration"),
         (migrations.Migration,),
         {
             "operations": [
                 migrations.DeleteModel("MyModel"),
                 migrations.AddField(
                     "OtherModel", "field_name", models.DateTimeField(default=datetime.datetime.utcnow)
                 ),
             ],
             "dependencies": [("testapp", "some_other_one")],
         },
     )
     writer = MigrationWriter(migration)
     output = writer.as_string()
     # It should NOT be unicode.
     self.assertIsInstance(output, six.binary_type, "Migration as_string returned unicode")
     # We don't test the output formatting - that's too fragile.
     # Just make sure it runs for now, and that things look alright.
     result = self.safe_exec(output)
     self.assertIn("Migration", result)
Example #2
0
    def test_migration(self):
        """
        Tests making migrations with Django 1.7+'s migration framework
        """

        import oscar
        from django.db import migrations
        from django.db.migrations.writer import MigrationWriter
        from django.utils import six
        from oscar.models.fields import AutoSlugField
        fields = {
            'autoslugfield': AutoSlugField(populate_from='otherfield'),
        }

        migration = type(str("Migration"), (migrations.Migration,), {
            "operations": [
                migrations.CreateModel("MyModel", tuple(fields.items()),
                                       {'populate_from': 'otherfield'},
                                       (models.Model,)),
            ],
        })
        writer = MigrationWriter(migration)
        output = writer.as_string()

        if isinstance(output, six.text_type):
            output = output.encode('utf-8')

        # We don't test the output formatting - that's too fragile.
        # Just make sure it runs for now, and that things look alright.
        context = {
            'migrations': migrations,
            'oscar': oscar,
        }
        result = self.safe_exec(output, context=context)
        self.assertIn("Migration", result)
    def test_total_deconstruct(self):
        loader = MigrationLoader(None, load=True, ignore_no_migrations=True)
        loader.disk_migrations = {t: v for t, v in loader.disk_migrations.items() if t[0] != 'testapp'}
        app_labels = {"testapp"}
        questioner = NonInteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=True)
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            questioner,
        )
        # Detect changes
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels or None,
            convert_apps=app_labels or None,
            migration_name="my_fake_migration_for_test_deconstruct",
        )
        self.assertGreater(len(changes), 0)
        for app_label, app_migrations in changes.items():
            for migration in app_migrations:
                # Describe the migration
                writer = MigrationWriter(migration)

                migration_string = writer.as_string()
                self.assertNotEqual(migration_string, "")
Example #4
0
 def test_serialize_multiline_strings(self):
     self.assertSerializedEqual(b"foo\nbar")
     string, imports = MigrationWriter.serialize(b"foo\nbar")
     self.assertEqual(string, "b'foo\\nbar'")
     self.assertSerializedEqual("föo\nbár")
     string, imports = MigrationWriter.serialize("foo\nbar")
     self.assertEqual(string, "'foo\\nbar'")
Example #5
0
    def test_simple_migration(self):
        """
        Tests serializing a simple migration.
        """
        fields = {
            'charfield': models.DateTimeField(default=datetime.datetime.utcnow),
            'datetimefield': models.DateTimeField(default=datetime.datetime.utcnow),
        }

        options = {
            'verbose_name': 'My model',
            'verbose_name_plural': 'My models',
        }

        migration = type("Migration", (migrations.Migration,), {
            "operations": [
                migrations.CreateModel("MyModel", tuple(fields.items()), options, (models.Model,)),
                migrations.CreateModel("MyModel2", tuple(fields.items()), bases=(models.Model,)),
                migrations.CreateModel(
                    name="MyModel3", fields=tuple(fields.items()), options=options, bases=(models.Model,)
                ),
                migrations.DeleteModel("MyModel"),
                migrations.AddField("OtherModel", "datetimefield", fields["datetimefield"]),
            ],
            "dependencies": [("testapp", "some_other_one")],
        })
        writer = MigrationWriter(migration)
        output = writer.as_string()
        # We don't test the output formatting - that's too fragile.
        # Just make sure it runs for now, and that things look alright.
        result = self.safe_exec(output)
        self.assertIn("Migration", result)
Example #6
0
 def write_migration_files(self, changes):
     """
     Takes a changes dict and writes them out as migration files.
     """
     directory_created = {}
     for app_label, app_migrations in changes.items():
         if self.verbosity >= 1:
             self.stdout.write(self.style.MIGRATE_HEADING("Migrations for '%s':" % app_label) + "\n")
         for migration in app_migrations:
             # Describe the migration
             writer = MigrationWriter(migration)
             if self.verbosity >= 1:
                 self.stdout.write("  %s:\n" % (self.style.MIGRATE_LABEL(writer.filename),))
                 for operation in migration.operations:
                     self.stdout.write("    - %s\n" % operation.describe())
             if not self.dry_run:
                 # Write the migrations file to the disk.
                 migrations_directory = os.path.dirname(writer.path)
                 if not directory_created.get(app_label, False):
                     if not os.path.isdir(migrations_directory):
                         os.mkdir(migrations_directory)
                     init_path = os.path.join(migrations_directory, "__init__.py")
                     if not os.path.isfile(init_path):
                         open(init_path, "w").close()
                     # We just do this once per app
                     directory_created[app_label] = True
                 migration_string = writer.as_string()
                 with open(writer.path, "wb") as fh:
                     fh.write(migration_string)
             elif self.verbosity == 3:
                 # Alternatively, makemigrations --dry-run --verbosity 3
                 # will output the migrations to stdout rather than saving
                 # the file to the disk.
                 self.stdout.write(self.style.MIGRATE_HEADING("Full migrations file '%s':" % writer.filename) + "\n")
                 self.stdout.write("%s\n" % writer.as_string())
    def test_17_migration(self):
        """
        Tests making migrations with Django 1.7+'s migration framework
        """

        fields = {
            'autoslugfield': AutoSlugField(populate_from='otherfield'),
        }

        migration = type(str("Migration"), (migrations.Migration,), {
            "operations": [
                migrations.CreateModel("MyModel", tuple(fields.items()),
                                       {'populate_from': 'otherfield'},
                                       (models.Model,)),
            ],
        })
        writer = MigrationWriter(migration)
        output = writer.as_string()
        # It should NOT be unicode.
        self.assertIsInstance(output, six.binary_type,
                              "Migration as_string returned unicode")
        # We don't test the output formatting - that's too fragile.
        # Just make sure it runs for now, and that things look alright.
        result = self.safe_exec(output)
        self.assertIn("Migration", result)
Example #8
0
def test_user_foreign_key_supports_migration():
    """Tests serializing UserForeignKey in a simple migration.

    Since `UserForeignKey` is a ForeignKey migrations pass `to=` explicitly
    and we have to pop it in our __init__.
    """
    fields = {
        'charfield': UserForeignKey(),
    }

    migration = type(str('Migration'), (migrations.Migration,), {
        'operations': [
            migrations.CreateModel(
                name='MyModel', fields=tuple(fields.items()),
                bases=(models.Model,)
            ),
        ],
    })
    writer = MigrationWriter(migration)
    output = writer.as_string()

    # Just make sure it runs and that things look alright.
    result = safe_exec(output, globals_=globals())

    assert 'Migration' in result
Example #9
0
 def handle_merge(self, loader, conflicts):
     """
     Handles merging together conflicted migrations interactively,
     if it's safe; otherwise, advises on how to fix it.
     """
     if self.interactive:
         questioner = InteractiveMigrationQuestioner()
     else:
         questioner = MigrationQuestioner(defaults={"ask_merge": True})
     for app_label, migration_names in conflicts.items():
         # Grab out the migrations in question, and work out their
         # common ancestor.
         merge_migrations = []
         for migration_name in migration_names:
             migration = loader.get_migration(app_label, migration_name)
             migration.ancestry = loader.graph.forwards_plan((app_label, migration_name))
             merge_migrations.append(migration)
         all_items_equal = lambda seq: all(item == seq[0] for item in seq[1:])
         merge_migrations_generations = zip(*[m.ancestry for m in merge_migrations])
         common_ancestor_count = sum(
             1 for common_ancestor_generation in takewhile(all_items_equal, merge_migrations_generations)
         )
         if not common_ancestor_count:
             raise ValueError("Could not find common ancestor of %s" % migration_names)
         # Now work out the operations along each divergent branch
         for migration in merge_migrations:
             migration.branch = migration.ancestry[common_ancestor_count:]
             migrations_ops = (
                 loader.get_migration(node_app, node_name).operations for node_app, node_name in migration.branch
             )
             migration.merged_operations = sum(migrations_ops, [])
         # In future, this could use some of the Optimizer code
         # (can_optimize_through) to automatically see if they're
         # mergeable. For now, we always just prompt the user.
         if self.verbosity > 0:
             self.stdout.write(self.style.MIGRATE_HEADING("Merging %s" % app_label))
             for migration in merge_migrations:
                 self.stdout.write(self.style.MIGRATE_LABEL("  Branch %s" % migration.name))
                 for operation in migration.merged_operations:
                     self.stdout.write("    - %s\n" % operation.describe())
         if questioner.ask_merge(app_label):
             # If they still want to merge it, then write out an empty
             # file depending on the migrations needing merging.
             numbers = [MigrationAutodetector.parse_number(migration.name) for migration in merge_migrations]
             try:
                 biggest_number = max([x for x in numbers if x is not None])
             except ValueError:
                 biggest_number = 1
             subclass = type(
                 "Migration",
                 (Migration,),
                 {"dependencies": [(app_label, migration.name) for migration in merge_migrations]},
             )
             new_migration = subclass("%04i_merge" % (biggest_number + 1), app_label)
             writer = MigrationWriter(new_migration)
             with open(writer.path, "wb") as fh:
                 fh.write(writer.as_string())
             if self.verbosity > 0:
                 self.stdout.write("\nCreated new merge migration %s" % writer.path)
Example #10
0
 def test_full_serialization(self):
     # ensure the values passed to kwarg arguments can be serialized
     # the recommended 'deconstruct' testing by django docs doesn't cut it
     # https://docs.djangoproject.com/en/1.7/howto/custom-model-fields/#field-deconstruction
     # replicates https://github.com/mfogel/django-timezone-field/issues/12
     for field in self.test_fields:
         # ensuring the following call doesn't throw an error
         MigrationWriter.serialize(field)
    def write_migration(self, migration):
        loader = MigrationLoader(None, ignore_no_migrations=True)
        autodetector = MigrationAutodetector(loader.project_state(), ProjectState.from_apps(apps),)
        changes = autodetector.arrange_for_graph(changes={'share': [migration]}, graph=loader.graph,)

        for m in changes['share']:
            writer = MigrationWriter(m)
            with open(writer.path, 'wb') as fp:
                fp.write(writer.as_string())
Example #12
0
    def test_register_serializer(self):
        class ComplexSerializer(BaseSerializer):
            def serialize(self):
                return 'complex(%r)' % self.value, {}

        MigrationWriter.register_serializer(complex, ComplexSerializer)
        self.assertSerializedEqual(complex(1, 2))
        MigrationWriter.unregister_serializer(complex)
        with self.assertRaisesMessage(ValueError, 'Cannot serialize: (1+2j)'):
            self.assertSerializedEqual(complex(1, 2))
Example #13
0
 def test_serialize(self):
     """
     Tests various different forms of the serializer.
     This does not care about formatting, just that the parsed result is
     correct, so we always exec() the result and check that.
     """
     # Basic values
     self.assertSerializedEqual(1)
     self.assertSerializedEqual(None)
     self.assertSerializedEqual(b"foobar")
     self.assertSerializedEqual("föobár")
     self.assertSerializedEqual({1: 2})
     self.assertSerializedEqual(["a", 2, True, None])
     self.assertSerializedEqual(set([2, 3, "eighty"]))
     self.assertSerializedEqual({"lalalala": ["yeah", "no", "maybe"]})
     self.assertSerializedEqual(_('Hello'))
     # Functions
     with six.assertRaisesRegex(self, ValueError, 'Cannot serialize function: lambda'):
         self.assertSerializedEqual(lambda x: 42)
     self.assertSerializedEqual(models.SET_NULL)
     string, imports = MigrationWriter.serialize(models.SET(42))
     self.assertEqual(string, 'models.SET(42)')
     self.serialize_round_trip(models.SET(42))
     # Datetime stuff
     self.assertSerializedEqual(datetime.datetime.utcnow())
     self.assertSerializedEqual(datetime.datetime.utcnow)
     self.assertSerializedEqual(datetime.datetime.today())
     self.assertSerializedEqual(datetime.datetime.today)
     self.assertSerializedEqual(datetime.date.today())
     self.assertSerializedEqual(datetime.date.today)
     # Classes
     validator = RegexValidator(message="hello")
     string, imports = MigrationWriter.serialize(validator)
     self.assertEqual(string, "django.core.validators.RegexValidator(message=%s)" % repr("hello"))
     self.serialize_round_trip(validator)
     validator = EmailValidator(message="hello")  # Test with a subclass.
     string, imports = MigrationWriter.serialize(validator)
     self.assertEqual(string, "django.core.validators.EmailValidator(message=%s)" % repr("hello"))
     self.serialize_round_trip(validator)
     validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello")
     string, imports = MigrationWriter.serialize(validator)
     self.assertEqual(string, "custom.EmailValidator(message=%s)" % repr("hello"))
     # Django fields
     self.assertSerializedFieldEqual(models.CharField(max_length=255))
     self.assertSerializedFieldEqual(models.TextField(null=True, blank=True))
     # Setting references
     self.assertSerializedEqual(SettingsReference(settings.AUTH_USER_MODEL, "AUTH_USER_MODEL"))
     self.assertSerializedResultEqual(
         SettingsReference("someapp.model", "AUTH_USER_MODEL"),
         (
             "settings.AUTH_USER_MODEL",
             set(["from django.conf import settings"]),
         )
     )
Example #14
0
    def test_serialize_enums(self):
        class TextEnum(enum.Enum):
            A = 'a-value'
            B = 'value-b'

        class BinaryEnum(enum.Enum):
            A = b'a-value'
            B = b'value-b'

        class IntEnum(enum.IntEnum):
            A = 1
            B = 2

        self.assertSerializedResultEqual(
            TextEnum.A,
            ("migrations.test_writer.TextEnum('a-value')", {'import migrations.test_writer'})
        )
        self.assertSerializedResultEqual(
            BinaryEnum.A,
            ("migrations.test_writer.BinaryEnum(b'a-value')", {'import migrations.test_writer'})
        )
        self.assertSerializedResultEqual(
            IntEnum.B,
            ("migrations.test_writer.IntEnum(2)", {'import migrations.test_writer'})
        )

        field = models.CharField(default=TextEnum.B, choices=[(m.value, m) for m in TextEnum])
        string = MigrationWriter.serialize(field)[0]
        self.assertEqual(
            string,
            "models.CharField(choices=["
            "('a-value', migrations.test_writer.TextEnum('a-value')), "
            "('value-b', migrations.test_writer.TextEnum('value-b'))], "
            "default=migrations.test_writer.TextEnum('value-b'))"
        )
        field = models.CharField(default=BinaryEnum.B, choices=[(m.value, m) for m in BinaryEnum])
        string = MigrationWriter.serialize(field)[0]
        self.assertEqual(
            string,
            "models.CharField(choices=["
            "(b'a-value', migrations.test_writer.BinaryEnum(b'a-value')), "
            "(b'value-b', migrations.test_writer.BinaryEnum(b'value-b'))], "
            "default=migrations.test_writer.BinaryEnum(b'value-b'))"
        )
        field = models.IntegerField(default=IntEnum.A, choices=[(m.value, m) for m in IntEnum])
        string = MigrationWriter.serialize(field)[0]
        self.assertEqual(
            string,
            "models.IntegerField(choices=["
            "(1, migrations.test_writer.IntEnum(1)), "
            "(2, migrations.test_writer.IntEnum(2))], "
            "default=migrations.test_writer.IntEnum(1))"
        )
Example #15
0
 def test_models_import_omitted(self):
     """
     django.db.models shouldn't be imported if unused.
     """
     migration = type("Migration", (migrations.Migration,), {
         "operations": [
             migrations.AlterModelOptions(
                 name='model',
                 options={'verbose_name': 'model', 'verbose_name_plural': 'models'},
             ),
         ]
     })
     writer = MigrationWriter(migration)
     output = writer.as_string()
     self.assertIn("from django.db import migrations\n", output)
Example #16
0
    def test_serialize_class_based_validators(self):
        """
        Ticket #22943: Test serialization of class-based validators, including
        compiled regexes.
        """
        validator = RegexValidator(message="hello")
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "django.core.validators.RegexValidator(message='hello')")
        self.serialize_round_trip(validator)

        # Test with a compiled regex.
        validator = RegexValidator(regex=re.compile(r'^\w+$'))
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "django.core.validators.RegexValidator(regex=re.compile('^\\\\w+$'))")
        self.serialize_round_trip(validator)

        # Test a string regex with flag
        validator = RegexValidator(r'^[0-9]+$', flags=re.S)
        string = MigrationWriter.serialize(validator)[0]
        if PY36:
            self.assertEqual(string, "django.core.validators.RegexValidator('^[0-9]+$', flags=re.RegexFlag(16))")
        else:
            self.assertEqual(string, "django.core.validators.RegexValidator('^[0-9]+$', flags=16)")
        self.serialize_round_trip(validator)

        # Test message and code
        validator = RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid')
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "django.core.validators.RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid')")
        self.serialize_round_trip(validator)

        # Test with a subclass.
        validator = EmailValidator(message="hello")
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "django.core.validators.EmailValidator(message='hello')")
        self.serialize_round_trip(validator)

        validator = deconstructible(path="migrations.test_writer.EmailValidator")(EmailValidator)(message="hello")
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "migrations.test_writer.EmailValidator(message='hello')")

        validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello")
        with self.assertRaisesMessage(ImportError, "No module named 'custom'"):
            MigrationWriter.serialize(validator)

        validator = deconstructible(path="django.core.validators.EmailValidator2")(EmailValidator)(message="hello")
        with self.assertRaisesMessage(ValueError, "Could not find object EmailValidator2 in django.core.validators."):
            MigrationWriter.serialize(validator)
Example #17
0
 def test_serialize(self):
     """
     Tests various different forms of the serializer.
     This does not care about formatting, just that the parsed result is
     correct, so we always exec() the result and check that.
     """
     # Basic values
     self.assertSerializedEqual(1)
     self.assertSerializedEqual(None)
     self.assertSerializedEqual(b"foobar")
     self.assertSerializedEqual("föobár")
     self.assertSerializedEqual({1: 2})
     self.assertSerializedEqual(["a", 2, True, None])
     self.assertSerializedEqual(set([2, 3, "eighty"]))
     self.assertSerializedEqual({"lalalala": ["yeah", "no", "maybe"]})
     self.assertSerializedEqual(_('Hello'))
     # Functions
     with six.assertRaisesRegex(self, ValueError, 'Cannot serialize function: lambda'):
         self.assertSerializedEqual(lambda x: 42)
     self.assertSerializedEqual(models.SET_NULL)
     string, imports = MigrationWriter.serialize(models.SET(42))
     self.assertEqual(string, 'models.SET(42)')
     self.serialize_round_trip(models.SET(42))
     # Datetime stuff
     self.assertSerializedEqual(datetime.datetime.utcnow())
     self.assertSerializedEqual(datetime.datetime.utcnow)
     self.assertSerializedEqual(datetime.datetime.today())
     self.assertSerializedEqual(datetime.datetime.today)
     self.assertSerializedEqual(datetime.date.today())
     self.assertSerializedEqual(datetime.date.today)
     # Django fields
     self.assertSerializedFieldEqual(models.CharField(max_length=255))
     self.assertSerializedFieldEqual(models.TextField(null=True, blank=True))
Example #18
0
    def test_register_serializer_for_migrations(self):
        tests = (
            (DateRange(empty=True), DateRangeField),
            (DateTimeRange(empty=True), DateRangeField),
            (DateTimeTZRange(None, None, '[]'), DateTimeRangeField),
            (NumericRange(1, 10), IntegerRangeField),
        )

        def assertNotSerializable():
            for default, test_field in tests:
                with self.subTest(default=default):
                    field = test_field(default=default)
                    with self.assertRaisesMessage(ValueError, 'Cannot serialize: %s' % default.__class__.__name__):
                        MigrationWriter.serialize(field)

        assertNotSerializable()
        with self.modify_settings(INSTALLED_APPS={'append': 'django.contrib.postgres'}):
            for default, test_field in tests:
                with self.subTest(default=default):
                    field = test_field(default=default)
                    serialized_field, imports = MigrationWriter.serialize(field)
                    self.assertEqual(imports, {
                        'import django.contrib.postgres.fields.ranges',
                        'import psycopg2.extras',
                    })
                    self.assertIn(
                        '%s.%s(default=psycopg2.extras.%r)' % (
                            field.__module__,
                            field.__class__.__name__,
                            default,
                        ),
                        serialized_field
                    )
        assertNotSerializable()
Example #19
0
def test_migration_serialization():
    imports = set(['import djmoney.models.fields'])
    if PY2:
        serialized = 'djmoney.models.fields.MoneyPatched(100, b\'GBP\')'
    else:
        serialized = 'djmoney.models.fields.MoneyPatched(100, \'GBP\')'
    assert MigrationWriter.serialize(MoneyPatched(100, 'GBP')) == (serialized, imports)
Example #20
0
 def test_serialize_functions(self):
     with six.assertRaisesRegex(self, ValueError, "Cannot serialize function: lambda"):
         self.assertSerializedEqual(lambda x: 42)
     self.assertSerializedEqual(models.SET_NULL)
     string, imports = MigrationWriter.serialize(models.SET(42))
     self.assertEqual(string, "models.SET(42)")
     self.serialize_round_trip(models.SET(42))
Example #21
0
 def test_serialize_functions(self):
     with self.assertRaisesMessage(ValueError, 'Cannot serialize function: lambda'):
         self.assertSerializedEqual(lambda x: 42)
     self.assertSerializedEqual(models.SET_NULL)
     string, imports = MigrationWriter.serialize(models.SET(42))
     self.assertEqual(string, 'models.SET(42)')
     self.serialize_round_trip(models.SET(42))
Example #22
0
 def write_migration_files(self, changes):
     """
     Take a changes dict and write them out as migration files.
     """
     directory_created = {}
     for app_label, app_migrations in changes.items():
         if self.verbosity >= 1:
             self.stdout.write(self.style.MIGRATE_HEADING("Migrations for '%s':" % app_label) + "\n")
         for migration in app_migrations:
             # Describe the migration
             writer = MigrationWriter(migration)
             if self.verbosity >= 1:
                 # Display a relative path if it's below the current working
                 # directory, or an absolute path otherwise.
                 try:
                     migration_string = os.path.relpath(writer.path)
                 except ValueError:
                     migration_string = writer.path
                 if migration_string.startswith('..'):
                     migration_string = writer.path
                 self.stdout.write("  %s\n" % (self.style.MIGRATE_LABEL(migration_string),))
                 for operation in migration.operations:
                     self.stdout.write("    - %s\n" % operation.describe())
             if not self.dry_run:
                 # Write the migrations file to the disk.
                 migrations_directory = os.path.dirname(writer.path)
                 if not directory_created.get(app_label):
                     if not os.path.isdir(migrations_directory):
                         os.mkdir(migrations_directory)
                     init_path = os.path.join(migrations_directory, "__init__.py")
                     if not os.path.isfile(init_path):
                         open(init_path, "w").close()
                     # We just do this once per app
                     directory_created[app_label] = True
                 migration_string = writer.as_string()
                 with open(writer.path, "w", encoding='utf-8') as fh:
                     fh.write(migration_string)
             elif self.verbosity == 3:
                 # Alternatively, makemigrations --dry-run --verbosity 3
                 # will output the migrations to stdout rather than saving
                 # the file to the disk.
                 self.stdout.write(self.style.MIGRATE_HEADING(
                     "Full migrations file '%s':" % writer.filename) + "\n"
                 )
                 self.stdout.write("%s\n" % writer.as_string())
Example #23
0
    def test_simple_migration(self):
        """
        Tests serializing a simple migration.
        """
        fields = {
            'charfield': models.DateTimeField(default=datetime.datetime.utcnow),
            'datetimefield': models.DateTimeField(default=datetime.datetime.utcnow),
        }

        options = {
            'verbose_name': 'My model',
            'verbose_name_plural': 'My models',
        }

        migration = type(str("Migration"), (migrations.Migration,), {
            "operations": [
                migrations.CreateModel("MyModel", tuple(fields.items()), options, (models.Model,)),
                migrations.CreateModel("MyModel2", tuple(fields.items()), bases=(models.Model,)),
                migrations.CreateModel(
                    name="MyModel3", fields=tuple(fields.items()), options=options, bases=(models.Model,)
                ),
                migrations.DeleteModel("MyModel"),
                migrations.AddField("OtherModel", "datetimefield", fields["datetimefield"]),
            ],
            "dependencies": [("testapp", "some_other_one")],
        })
        writer = MigrationWriter(migration)
        output = writer.as_string()
        # It should NOT be unicode.
        self.assertIsInstance(output, six.binary_type, "Migration as_string returned unicode")
        # We don't test the output formatting - that's too fragile.
        # Just make sure it runs for now, and that things look alright.
        result = self.safe_exec(output)
        self.assertIn("Migration", result)
        # In order to preserve compatibility with Python 3.2 unicode literals
        # prefix shouldn't be added to strings.
        tokens = tokenize.generate_tokens(six.StringIO(str(output)).readline)
        for token_type, token_source, (srow, scol), __, line in tokens:
            if token_type == tokenize.STRING:
                self.assertFalse(
                    token_source.startswith('u'),
                    "Unicode literal prefix found at %d:%d: %r" % (
                        srow, scol, line.strip()
                    )
                )
	def test_migration(self):
		fields = {
			'field1':EnumField(TestEnum)
		}
		migration = type(str("Migration"), (migrations.Migration,), {
			'operations' : [
				migrations.CreateModel("Model1", tuple(fields.items()), {}, (models.Model))
			]
		})
		writer = MigrationWriter(migration)
		output = writer.as_string()
		self.assertIsInstance(output, six.binary_type)
		r = {}
		try:
			exec(output, globals(), r)
		except Exception as e:
			self.fail("Could not exec {!r}: {}".format(output.strip(), e))
		self.assertIn("Migration", r)
Example #25
0
def uninstall_if_needed(setting, value, enter, **kwargs):
    """
    Undo the effects of PostgresConfig.ready() when django.contrib.postgres
    is "uninstalled" by override_settings().
    """
    if not enter and setting == 'INSTALLED_APPS' and 'django.contrib.postgres' not in set(value):
        connection_created.disconnect(register_type_handlers)
        CharField._unregister_lookup(Unaccent)
        TextField._unregister_lookup(Unaccent)
        CharField._unregister_lookup(SearchLookup)
        TextField._unregister_lookup(SearchLookup)
        CharField._unregister_lookup(TrigramSimilar)
        TextField._unregister_lookup(TrigramSimilar)
        # Disconnect this receiver until the next time this app is installed
        # and ready() connects it again to prevent unnecessary processing on
        # each setting change.
        setting_changed.disconnect(uninstall_if_needed)
        MigrationWriter.unregister_serializer(RANGE_TYPES)
Example #26
0
 def test_custom_operation(self):
     migration = type("Migration", (migrations.Migration,), {
         "operations": [
             custom_migration_operations.operations.TestOperation(),
             custom_migration_operations.operations.CreateModel(),
             migrations.CreateModel("MyModel", (), {}, (models.Model,)),
             custom_migration_operations.more_operations.TestOperation()
         ],
         "dependencies": []
     })
     writer = MigrationWriter(migration)
     output = writer.as_string()
     result = self.safe_exec(output)
     self.assertIn("custom_migration_operations", result)
     self.assertNotEqual(
         result['custom_migration_operations'].operations.TestOperation,
         result['custom_migration_operations'].more_operations.TestOperation
     )
Example #27
0
    def test_serialize_datetime(self):
        """
        #23365 -- Timezone-aware datetimes should be allowed.
        """
        # naive datetime
        naive_datetime = datetime.datetime(2014, 1, 1, 1, 1)
        self.assertEqual(MigrationWriter.serialize_datetime(naive_datetime),
                         "datetime.datetime(2014, 1, 1, 1, 1)")

        # datetime with utc timezone
        utc_datetime = datetime.datetime(2014, 1, 1, 1, 1, tzinfo=utc)
        self.assertEqual(MigrationWriter.serialize_datetime(utc_datetime),
                         "datetime.datetime(2014, 1, 1, 1, 1, tzinfo=utc)")

        # datetime with FixedOffset tzinfo
        fixed_offset_datetime = datetime.datetime(2014, 1, 1, 1, 1, tzinfo=FixedOffset(180))
        self.assertEqual(MigrationWriter.serialize_datetime(fixed_offset_datetime),
                         "datetime.datetime(2013, 12, 31, 22, 1, tzinfo=utc)")
Example #28
0
 def test_sorted_imports(self):
     """
     #24155 - Tests ordering of imports.
     """
     migration = type("Migration", (migrations.Migration,), {
         "operations": [
             migrations.AddField("mymodel", "myfield", models.DateTimeField(
                 default=datetime.datetime(2012, 1, 1, 1, 1, tzinfo=utc),
             )),
         ]
     })
     writer = MigrationWriter(migration)
     output = writer.as_string()
     self.assertIn(
         "import datetime\n"
         "from django.db import migrations, models\n"
         "from django.utils.timezone import utc\n",
         output
     )
Example #29
0
    def test_migration_file_header_comments(self):
        """
        Test comments at top of file.
        """
        migration = type("Migration", (migrations.Migration,), {
            "operations": []
        })
        dt = datetime.datetime(2015, 7, 31, 4, 40, 0, 0, tzinfo=utc)
        with mock.patch('django.db.migrations.writer.now', lambda: dt):
            writer = MigrationWriter(migration)
            output = writer.as_string()

        self.assertTrue(
            output.startswith(
                "# Generated by Django %(version)s on 2015-07-31 04:40\n" % {
                    'version': get_version(),
                }
            )
        )
Example #30
0
    def test_deconstruct_class_arguments(self):
        # Yes, it doesn't make sense to use a class as a default for a
        # CharField. It does make sense for custom fields though, for example
        # an enumfield that takes the enum class as an argument.
        class DeconstructibleInstances:
            def deconstruct(self):
                return ('DeconstructibleInstances', [], {})

        string = MigrationWriter.serialize(models.CharField(default=DeconstructibleInstances))[0]
        self.assertEqual(string, "models.CharField(default=migrations.test_writer.DeconstructibleInstances)")
    def test_makemigrations_with_size(self):
        field = ListTextField(
            models.CharField(max_length=5),
            max_length=32,
            size=5
        )
        statement, imports = MigrationWriter.serialize(field)

        # The order of the output max_length/size statements varies by
        # python version, hence a little regexp to match them
        assert re.compile(
            r"""^django_mysql\.models\.ListTextField\(
                models\.CharField\(max_length=5\),\ # space here
                (
                    max_length=32,\ size=5|
                    size=5,\ max_length=32
                )
                \)$
            """,
            re.VERBOSE
        ).match(statement)
Example #32
0
    def test_deconstruct_default_arguments(self):

        svf = SearchVectorField([
            WeightedColumn('name', 'A'),
            WeightedColumn('description', 'D'),
        ], language=None, language_column=None, force_update=False)

        definition, path = MigrationWriter.serialize(svf)

        self.assertEqual(
            "tsvector_field.SearchVectorField("
            "columns=["
            "tsvector_field.WeightedColumn('name', 'A'), "
            "tsvector_field.WeightedColumn('description', 'D')]"
            ")",
            definition
        )

        self.assertSetEqual(
            {'import tsvector_field'},
            path
        )
Example #33
0
    def test_serialize_uuid(self):
        self.assertSerializedEqual(uuid.uuid1())
        self.assertSerializedEqual(uuid.uuid4())

        uuid_a = uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8')
        uuid_b = uuid.UUID('c7853ec1-2ea3-4359-b02d-b54e8f1bcee2')
        self.assertSerializedResultEqual(
            uuid_a, ("uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8')",
                     {'import uuid'}))
        self.assertSerializedResultEqual(
            uuid_b, ("uuid.UUID('c7853ec1-2ea3-4359-b02d-b54e8f1bcee2')",
                     {'import uuid'}))

        field = models.UUIDField(choices=((uuid_a, 'UUID A'), (uuid_b,
                                                               'UUID B')),
                                 default=uuid_a)
        string = MigrationWriter.serialize(field)[0]
        self.assertEqual(
            string, "models.UUIDField(choices=["
            "(uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8'), 'UUID A'), "
            "(uuid.UUID('c7853ec1-2ea3-4359-b02d-b54e8f1bcee2'), 'UUID B')], "
            "default=uuid.UUID('5c859437-d061-4847-b3f7-e6b78852f8c8'))")
Example #34
0
    def test_migration_path(self):
        test_apps = [
            'migrations.migrations_test_apps.normal',
            'migrations.migrations_test_apps.with_package_model',
            'migrations.migrations_test_apps.without_init_file',
        ]

        base_dir = os.path.dirname(os.path.dirname(__file__))

        for app in test_apps:
            with self.modify_settings(INSTALLED_APPS={'append': app}):
                migration = migrations.Migration('0001_initial',
                                                 app.split('.')[-1])
                expected_path = os.path.join(
                    base_dir,
                    *(app.split('.') + ['migrations', '0001_initial.py']))
                writer = MigrationWriter(migration)
                # Silence warning on Python 2: Not importing directory
                # 'tests/migrations/migrations_test_apps/without_init_file/migrations':
                # missing __init__.py
                with warnings.catch_warnings():
                    warnings.filterwarnings("ignore", category=ImportWarning)
                    self.assertEqual(writer.path, expected_path)
Example #35
0
    def test_migration_path(self):
        _old_app_store = copy.deepcopy(cache.app_store)

        test_apps = [
            'migrations.migrations_test_apps.normal',
            'migrations.migrations_test_apps.with_package_model',
        ]

        base_dir = os.path.dirname(os.path.dirname(__file__))

        try:
            with override_settings(INSTALLED_APPS=test_apps):
                for app in test_apps:
                    cache.load_app(app)
                    migration = migrations.Migration('0001_initial',
                                                     app.split('.')[-1])
                    expected_path = os.path.join(
                        base_dir,
                        *(app.split('.') + ['migrations', '0001_initial.py']))
                    writer = MigrationWriter(migration)
                    self.assertEqual(writer.path, expected_path)
        finally:
            cache.app_store = _old_app_store
Example #36
0
    def test_register_serializer_for_migrations(self):
        tests = (
            (DateRange(empty=True), DateRangeField),
            (DateTimeRange(empty=True), DateRangeField),
            (DateTimeTZRange(None, None, '[]'), DateTimeRangeField),
            (NumericRange(1, 10), IntegerRangeField),
        )

        def assertNotSerializable():
            for default, test_field in tests:
                with self.subTest(default=default):
                    field = test_field(default=default)
                    with self.assertRaisesMessage(
                            ValueError, 'Cannot serialize: %s' %
                            default.__class__.__name__):
                        MigrationWriter.serialize(field)

        assertNotSerializable()
        with self.modify_settings(
                INSTALLED_APPS={'append': 'django.contrib.postgres'}):
            for default, test_field in tests:
                with self.subTest(default=default):
                    field = test_field(default=default)
                    serialized_field, imports = MigrationWriter.serialize(
                        field)
                    self.assertEqual(
                        imports, {
                            'import django.contrib.postgres.fields.ranges',
                            'import psycopg2.extras',
                        })
                    self.assertIn(
                        '%s.%s(default=psycopg2.extras.%r)' % (
                            field.__module__,
                            field.__class__.__name__,
                            default,
                        ), serialized_field)
        assertNotSerializable()
Example #37
0
 def test_serialize_complex_func_index(self):
     index = models.Index(
         models.Func('rating', function='ABS'),
         models.Case(
             models.When(name='special', then=models.Value('X')),
             default=models.Value('other'),
         ),
         models.ExpressionWrapper(
             models.F('pages'),
             output_field=models.IntegerField(),
         ),
         models.OrderBy(models.F('name').desc()),
         name='complex_func_index',
     )
     string, imports = MigrationWriter.serialize(index)
     self.assertEqual(
         string, "models.Index(models.Func('rating', function='ABS'), "
         "models.Case(models.When(name='special', then=models.Value('X')), "
         "default=models.Value('other')), "
         "models.ExpressionWrapper("
         "models.F('pages'), output_field=models.IntegerField()), "
         "models.OrderBy(models.OrderBy(models.F('name'), descending=True)), "
         "name='complex_func_index')")
     self.assertEqual(imports, {'from django.db import models'})
Example #38
0
    def test_serialize_class_based_validators(self):
        """
        Ticket #22943: Test serialization of class-based validators, including
        compiled regexes.
        """
        validator = RegexValidator(message="hello")
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "django.core.validators.RegexValidator(message='hello')")
        self.serialize_round_trip(validator)

        # Test with a compiled regex.
        validator = RegexValidator(regex=re.compile(r'^\w+$', re.U))
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "django.core.validators.RegexValidator(regex=re.compile('^\\\\w+$', 32))")
        self.serialize_round_trip(validator)

        # Test a string regex with flag
        validator = RegexValidator(r'^[0-9]+$', flags=re.U)
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "django.core.validators.RegexValidator('^[0-9]+$', flags=32)")
        self.serialize_round_trip(validator)

        # Test message and code
        validator = RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid')
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "django.core.validators.RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid')")
        self.serialize_round_trip(validator)

        # Test with a subclass.
        validator = EmailValidator(message="hello")
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "django.core.validators.EmailValidator(message='hello')")
        self.serialize_round_trip(validator)

        validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello")
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(string, "custom.EmailValidator(message='hello')")
Example #39
0
 def assertSerializedResultEqual(self, value, target):
     self.assertEqual(MigrationWriter.serialize(value), target)
Example #40
0
    def get_text_for_value(self, value):
        return ', '.join(
            [self.choicelist.get_text_for_value(bc.value) for bc in value])


# from lino.core.choicelists import Choice, ChoiceList, CallableChoice


class ChoiceSerializer(BaseSerializer):
    def serialize(self):
        return ("rt.models.{}.{}.get_by_value('{}')".format(
            self.value.choicelist.app_label, self.value.choicelist.__name__,
            self.value.value), {'from lino.api.shell import rt'})


MigrationWriter.register_serializer(Choice, ChoiceSerializer)


class ChoiceListSerializer(BaseSerializer):
    def serialize(self):
        return ("rt.models.{}.{}".format(self.value.choicelist.app_label,
                                         self.value.choicelist.__name__),
                {'from lino.api.shell import rt'})


MigrationWriter.register_serializer(ChoiceList, ChoiceListSerializer)


class CallableChoiceSerializer(BaseSerializer):
    def serialize(self):
        choice = self.value()
    def handle(self, **options):

        self.verbosity = options['verbosity']
        self.interactive = options['interactive']
        app_label = options['app_label']
        start_migration_name = options['start_migration_name']
        migration_name = options['migration_name']
        no_optimize = options['no_optimize']
        squashed_name = options['squashed_name']
        include_header = options['include_header']
        # Validate app_label.
        try:
            apps.get_app_config(app_label)
        except LookupError as err:
            raise CommandError(str(err))
        # Load the current graph state, check the BLOG and migration they asked for exists
        loader = MigrationLoader(connections[DEFAULT_DB_ALIAS])
        if app_label not in loader.migrated_apps:
            raise CommandError(
                "App '%s' does not have migrations (so squashmigrations on "
                "it makes no sense)" % app_label)

        migration = self.find_migration(loader, app_label, migration_name)

        # Work out the list of predecessor migrations
        migrations_to_squash = [
            loader.get_migration(al, mn)
            for al, mn in loader.graph.forwards_plan((migration.app_label,
                                                      migration.name))
            if al == migration.app_label
        ]

        if start_migration_name:
            start_migration = self.find_migration(loader, app_label,
                                                  start_migration_name)
            start = loader.get_migration(start_migration.app_label,
                                         start_migration.name)
            try:
                start_index = migrations_to_squash.index(start)
                migrations_to_squash = migrations_to_squash[start_index:]
            except ValueError:
                raise CommandError(
                    "The migration '%s' cannot be found. Maybe it comes after "
                    "the migration '%s'?\n"
                    "Have a look at:\n"
                    "  python manage.py showmigrations %s\n"
                    "to debug this issue." %
                    (start_migration, migration, app_label))

        # Tell them what we're doing and optionally ask if we should proceed
        if self.verbosity > 0 or self.interactive:
            self.stdout.write(
                self.style.MIGRATE_HEADING(
                    "Will squash the following migrations:"))
            for migration in migrations_to_squash:
                self.stdout.write(" - %s" % migration.name)

            if self.interactive:
                answer = None
                while not answer or answer not in "yn":
                    answer = input("Do you wish to proceed? [yN] ")
                    if not answer:
                        answer = "n"
                        break
                    else:
                        answer = answer[0].lower()
                if answer != "y":
                    return

        # Load the operations from all those migrations and concat together,
        # along with collecting external dependencies and detecting
        # double-squashing
        operations = []
        dependencies = set()
        # We need to take all dependencies from the first migration in the list
        # as it may be 0002 depending on 0001
        first_migration = True
        for smigration in migrations_to_squash:
            if smigration.replaces:
                raise CommandError(
                    "You cannot squash squashed migrations! Please transition "
                    "it to a normal migration first: "
                    "https://docs.djangoproject.com/en/%s/topics/migrations/#squashing-migrations"
                    % get_docs_version())
            operations.extend(smigration.operations)
            for dependency in smigration.dependencies:
                if isinstance(dependency, SwappableTuple):
                    if settings.AUTH_USER_MODEL == dependency.setting:
                        dependencies.add(("__setting__", "AUTH_USER_MODEL"))
                    else:
                        dependencies.add(dependency)
                elif dependency[0] != smigration.app_label or first_migration:
                    dependencies.add(dependency)
            first_migration = False

        if no_optimize:
            if self.verbosity > 0:
                self.stdout.write(
                    self.style.MIGRATE_HEADING("(Skipping optimization.)"))
            new_operations = operations
        else:
            if self.verbosity > 0:
                self.stdout.write(self.style.MIGRATE_HEADING("Optimizing..."))

            optimizer = MigrationOptimizer()
            new_operations = optimizer.optimize(operations,
                                                migration.app_label)

            if self.verbosity > 0:
                if len(new_operations) == len(operations):
                    self.stdout.write("  No optimizations possible.")
                else:
                    self.stdout.write(
                        "  Optimized from %s operations to %s operations." %
                        (len(operations), len(new_operations)))

        # Work out the value of replaces (any squashed ones we're re-squashing)
        # need to feed their replaces into ours
        replaces = []
        for migration in migrations_to_squash:
            if migration.replaces:
                replaces.extend(migration.replaces)
            else:
                replaces.append((migration.app_label, migration.name))

        # Make a new migration with those operations
        subclass = type(
            "Migration", (migrations.Migration, ), {
                "dependencies": dependencies,
                "operations": new_operations,
                "replaces": replaces,
            })
        if start_migration_name:
            if squashed_name:
                # Use the name from --squashed-name.
                prefix, _ = start_migration.name.split('_', 1)
                name = '%s_%s' % (prefix, squashed_name)
            else:
                # Generate a name.
                name = '%s_squashed_%s' % (start_migration.name,
                                           migration.name)
            new_migration = subclass(name, app_label)
        else:
            name = '0001_%s' % (squashed_name
                                or 'squashed_%s' % migration.name)
            new_migration = subclass(name, app_label)
            new_migration.initial = True

        # Write out the new migration file
        writer = MigrationWriter(new_migration, include_header)
        with open(writer.path, "w", encoding='utf-8') as fh:
            fh.write(writer.as_string())

        if self.verbosity > 0:
            self.stdout.write(
                self.style.MIGRATE_HEADING(
                    'Created new squashed migration %s' % writer.path) + '\n'
                '  You should commit this migration but leave the old ones in place;\n'
                '  the new migration will be used for new installs. Once you are sure\n'
                '  all instances of the codebase have applied the migrations you squashed,\n'
                '  you can delete them.')
            if writer.needs_manual_porting:
                self.stdout.write(
                    self.style.MIGRATE_HEADING('Manual porting required') +
                    '\n'
                    '  Your migrations contained functions that must be manually copied over,\n'
                    '  as we could not safely copy their implementation.\n'
                    '  See the comment at the top of the squashed migration for details.'
                )
Example #42
0
 def test_serialize_range(self):
     string, imports = MigrationWriter.serialize(range(1, 5))
     self.assertEqual(string, "range(1, 5)")
     self.assertEqual(imports, set())
Example #43
0
from django.core.files.storage import FileSystemStorage

from django.db.migrations.serializer import BaseSerializer
from django.db.migrations.writer import MigrationWriter


class FileSystemStorageSerializer(BaseSerializer):
    def serialize(self):
        return self.value.location, {
            'from django.core.files.storage import FileSystemStorage'
        }


MigrationWriter.register_serializer(FileSystemStorage,
                                    FileSystemStorageSerializer)
Example #44
0
 def test_serialize_builtins(self):
     string, imports = MigrationWriter.serialize(range)
     self.assertEqual(string, 'range')
     self.assertEqual(imports, set())
Example #45
0
 def test_makemigrations(self):
     field = DynamicField(spec={'a': 'beta'})
     statement, imports = MigrationWriter.serialize(field)
     # 'spec' should not appear since that would trigger needless ALTERs
     assert statement == "django_mysql.models.DynamicField()"
Example #46
0
    def test_black_and_isort(self):
        writer = MigrationWriter(self.get_migration())
        output = writer.as_string()
        expected = textwrap.dedent("""
                import datetime

                from django.db import migrations, models


                class Migration(migrations.Migration):

                    dependencies = [
                        ("testapp", "some_other_one"),
                    ]

                    operations = [
                        migrations.CreateModel(
                            name="MyModel",
                            fields=[
                                ("charfield", models.DateTimeField(default=datetime.datetime.utcnow)),
                                (
                                    "datetimefield",
                                    models.DateTimeField(default=datetime.datetime.utcnow),
                                ),
                            ],
                            options={
                                "verbose_name": "My model",
                                "verbose_name_plural": "My models",
                            },
                        ),
                        migrations.CreateModel(
                            name="MyModel2",
                            fields=[
                                ("charfield", models.DateTimeField(default=datetime.datetime.utcnow)),
                                (
                                    "datetimefield",
                                    models.DateTimeField(default=datetime.datetime.utcnow),
                                ),
                            ],
                        ),
                        migrations.CreateModel(
                            name="MyModel3",
                            fields=[
                                ("charfield", models.DateTimeField(default=datetime.datetime.utcnow)),
                                (
                                    "datetimefield",
                                    models.DateTimeField(default=datetime.datetime.utcnow),
                                ),
                            ],
                            options={
                                "verbose_name": "My model",
                                "verbose_name_plural": "My models",
                            },
                        ),
                        migrations.DeleteModel(
                            name="MyModel",
                        ),
                        migrations.AddField(
                            model_name="OtherModel",
                            name="datetimefield",
                            field=models.DateTimeField(default=datetime.datetime.utcnow),
                        ),
                    ]
            """

                                   # noqa
                                   )
        self.assertInOutput(expected, output)
Example #47
0
 def test_serialize(self):
     """
     Tests various different forms of the serializer.
     This does not care about formatting, just that the parsed result is
     correct, so we always exec() the result and check that.
     """
     # Basic values
     self.assertSerializedEqual(1)
     self.assertSerializedEqual(None)
     self.assertSerializedEqual(b"foobar")
     string, imports = MigrationWriter.serialize(b"foobar")
     self.assertEqual(string, "b'foobar'")
     self.assertSerializedEqual("föobár")
     string, imports = MigrationWriter.serialize("foobar")
     self.assertEqual(string, "'foobar'")
     self.assertSerializedEqual({1: 2})
     self.assertSerializedEqual(["a", 2, True, None])
     self.assertSerializedEqual(set([2, 3, "eighty"]))
     self.assertSerializedEqual({"lalalala": ["yeah", "no", "maybe"]})
     self.assertSerializedEqual(_('Hello'))
     # Functions
     with six.assertRaisesRegex(self, ValueError,
                                'Cannot serialize function: lambda'):
         self.assertSerializedEqual(lambda x: 42)
     self.assertSerializedEqual(models.SET_NULL)
     string, imports = MigrationWriter.serialize(models.SET(42))
     self.assertEqual(string, 'models.SET(42)')
     self.serialize_round_trip(models.SET(42))
     # Datetime stuff
     self.assertSerializedEqual(datetime.datetime.utcnow())
     self.assertSerializedEqual(datetime.datetime.utcnow)
     self.assertSerializedEqual(datetime.datetime.today())
     self.assertSerializedEqual(datetime.datetime.today)
     self.assertSerializedEqual(datetime.date.today())
     self.assertSerializedEqual(datetime.date.today)
     with self.assertRaises(ValueError):
         self.assertSerializedEqual(
             datetime.datetime(2012,
                               1,
                               1,
                               1,
                               1,
                               tzinfo=get_default_timezone()))
     safe_date = datetime_safe.date(2014, 3, 31)
     string, imports = MigrationWriter.serialize(safe_date)
     self.assertEqual(string, repr(datetime.date(2014, 3, 31)))
     self.assertEqual(imports, {'import datetime'})
     safe_datetime = datetime_safe.datetime(2014, 3, 31, 16, 4, 31)
     string, imports = MigrationWriter.serialize(safe_datetime)
     self.assertEqual(string,
                      repr(datetime.datetime(2014, 3, 31, 16, 4, 31)))
     self.assertEqual(imports, {'import datetime'})
     # Classes
     validator = RegexValidator(message="hello")
     string, imports = MigrationWriter.serialize(validator)
     self.assertEqual(
         string, "django.core.validators.RegexValidator(message='hello')")
     self.serialize_round_trip(validator)
     validator = EmailValidator(message="hello")  # Test with a subclass.
     string, imports = MigrationWriter.serialize(validator)
     self.assertEqual(
         string, "django.core.validators.EmailValidator(message='hello')")
     self.serialize_round_trip(validator)
     validator = deconstructible(
         path="custom.EmailValidator")(EmailValidator)(message="hello")
     string, imports = MigrationWriter.serialize(validator)
     self.assertEqual(string, "custom.EmailValidator(message='hello')")
     # Django fields
     self.assertSerializedFieldEqual(models.CharField(max_length=255))
     self.assertSerializedFieldEqual(models.TextField(null=True,
                                                      blank=True))
     # Setting references
     self.assertSerializedEqual(
         SettingsReference(settings.AUTH_USER_MODEL, "AUTH_USER_MODEL"))
     self.assertSerializedResultEqual(
         SettingsReference("someapp.model", "AUTH_USER_MODEL"), (
             "settings.AUTH_USER_MODEL",
             set(["from django.conf import settings"]),
         ))
     self.assertSerializedResultEqual(((x, x * x) for x in range(3)), (
         "((0, 0), (1, 1), (2, 4))",
         set(),
     ))
Example #48
0
 def test_register_non_serializer(self):
     with self.assertRaisesMessage(
         ValueError, "'TestModel1' must inherit from 'BaseSerializer'."
     ):
         MigrationWriter.register_serializer(complex, TestModel1)
Example #49
0
 def test_serialize_type_model(self):
     self.assertSerializedEqual(models.Model)
     self.assertSerializedResultEqual(
         MigrationWriter.serialize(models.Model),
         ("('models.Model', {'from django.db import models'})", set()),
     )
Example #50
0
def test_migration_serialization():
    serialized = "djmoney.money.Money(100, 'GBP')"
    assert MigrationWriter.serialize(Money(
        100, "GBP")) == (serialized, {"import djmoney.money"})
Example #51
0
    def test_serialize_class_based_validators(self):
        """
        Ticket #22943: Test serialization of class-based validators, including
        compiled regexes.
        """
        validator = RegexValidator(message="hello")
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(
            string, "django.core.validators.RegexValidator(message='hello')")
        self.serialize_round_trip(validator)

        # Test with a compiled regex.
        validator = RegexValidator(regex=re.compile(r'^\w+$'))
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(
            string,
            "django.core.validators.RegexValidator(regex=re.compile('^\\\\w+$'))"
        )
        self.serialize_round_trip(validator)

        # Test a string regex with flag
        validator = RegexValidator(r'^[0-9]+$', flags=re.S)
        string = MigrationWriter.serialize(validator)[0]
        if PY36:
            self.assertEqual(
                string,
                "django.core.validators.RegexValidator('^[0-9]+$', flags=re.RegexFlag(16))"
            )
        else:
            self.assertEqual(
                string,
                "django.core.validators.RegexValidator('^[0-9]+$', flags=16)")
        self.serialize_round_trip(validator)

        # Test message and code
        validator = RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid')
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(
            string,
            "django.core.validators.RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid')"
        )
        self.serialize_round_trip(validator)

        # Test with a subclass.
        validator = EmailValidator(message="hello")
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(
            string, "django.core.validators.EmailValidator(message='hello')")
        self.serialize_round_trip(validator)

        validator = deconstructible(
            path="migrations.test_writer.EmailValidator")(EmailValidator)(
                message="hello")
        string = MigrationWriter.serialize(validator)[0]
        self.assertEqual(
            string, "migrations.test_writer.EmailValidator(message='hello')")

        validator = deconstructible(
            path="custom.EmailValidator")(EmailValidator)(message="hello")
        with self.assertRaisesMessage(ImportError, "No module named 'custom'"):
            MigrationWriter.serialize(validator)

        validator = deconstructible(
            path="django.core.validators.EmailValidator2")(EmailValidator)(
                message="hello")
        with self.assertRaisesMessage(
                ValueError,
                "Could not find object EmailValidator2 in django.core.validators."
        ):
            MigrationWriter.serialize(validator)
Example #52
0
    def handle_merge(self, loader, conflicts):
        """
        Handles merging together conflicted migrations interactively,
        if it's safe; otherwise, advises on how to fix it.
        """
        if self.interactive:
            questioner = InteractiveMigrationQuestioner()
        else:
            questioner = MigrationQuestioner(defaults={"ask_merge": True})

        for app_label, migration_names in conflicts.items():
            # Grab out the migrations in question, and work out their
            # common ancestor.
            merge_migrations = []
            for migration_name in migration_names:
                migration = loader.get_migration(app_label, migration_name)
                migration.ancestry = [
                    mig for mig in loader.graph.forwards_plan((app_label,
                                                               migration_name))
                    if mig[0] == migration.app_label
                ]
                merge_migrations.append(migration)

            def all_items_equal(seq):
                return all(item == seq[0] for item in seq[1:])

            merge_migrations_generations = zip(*(m.ancestry
                                                 for m in merge_migrations))
            common_ancestor_count = sum(
                1 for common_ancestor_generation in takewhile(
                    all_items_equal, merge_migrations_generations))
            if not common_ancestor_count:
                raise ValueError("Could not find common ancestor of %s" %
                                 migration_names)
            # Now work out the operations along each divergent branch
            for migration in merge_migrations:
                migration.branch = migration.ancestry[common_ancestor_count:]
                migrations_ops = (loader.get_migration(node_app,
                                                       node_name).operations
                                  for node_app, node_name in migration.branch)
                migration.merged_operations = sum(migrations_ops, [])
            # In future, this could use some of the Optimizer code
            # (can_optimize_through) to automatically see if they're
            # mergeable. For now, we always just prompt the user.
            if self.verbosity > 0:
                self.stdout.write(
                    self.style.MIGRATE_HEADING("Merging %s" % app_label))
                for migration in merge_migrations:
                    self.stdout.write(
                        self.style.MIGRATE_LABEL("  Branch %s" %
                                                 migration.name))
                    for operation in migration.merged_operations:
                        self.stdout.write("    - %s" % operation.describe())
            if questioner.ask_merge(app_label):
                # If they still want to merge it, then write out an empty
                # file depending on the migrations needing merging.
                numbers = [
                    MigrationAutodetector.parse_number(migration.name)
                    for migration in merge_migrations
                ]
                try:
                    biggest_number = max(x for x in numbers if x is not None)
                except ValueError:
                    biggest_number = 1
                subclass = type(
                    "Migration",
                    (Migration, ),
                    {
                        "dependencies": [(app_label, migration.name)
                                         for migration in merge_migrations],
                    },
                )
                migration_name = "%04i_%s" % (
                    biggest_number + 1,
                    self.migration_name or
                    ("merge_%s" % get_migration_name_timestamp()),
                )
                new_migration = subclass(migration_name, app_label)
                writer = MigrationWriter(new_migration, self.include_header)

                if not self.dry_run:
                    # Write the merge migrations file to the disk
                    with open(writer.path, "w", encoding="utf-8") as fh:
                        fh.write(writer.as_string())
                    if self.verbosity > 0:
                        self.stdout.write("\nCreated new merge migration %s" %
                                          writer.path)
                elif self.verbosity == 3:
                    # Alternatively, makemigrations --merge --dry-run --verbosity 3
                    # will output the merge migrations to stdout rather than saving
                    # the file to the disk.
                    self.stdout.write(
                        self.style.MIGRATE_HEADING(
                            "Full merge migrations file '%s':" %
                            writer.filename))
                    self.stdout.write(writer.as_string())
Example #53
0
    def handle(self, **options):

        self.verbosity = options.get('verbosity')
        self.interactive = options.get('interactive')
        app_label, migration_name = options['app_label'], options[
            'migration_name']

        # Load the current graph state, check the app and migration they asked for exists
        executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
        if app_label not in executor.loader.migrated_apps:
            raise CommandError(
                "App '%s' does not have migrations (so squashmigrations on it makes no sense)"
                % app_label)
        try:
            migration = executor.loader.get_migration_by_prefix(
                app_label, migration_name)
        except AmbiguityError:
            raise CommandError(
                "More than one migration matches '%s' in app '%s'. Please be more specific."
                % (migration_name, app_label))
        except KeyError:
            raise CommandError(
                "Cannot find a migration matching '%s' from app '%s'." %
                (migration_name, app_label))

        # Work out the list of predecessor migrations
        migrations_to_squash = [
            executor.loader.get_migration(al, mn)
            for al, mn in executor.loader.graph.forwards_plan((
                migration.app_label, migration.name))
            if al == migration.app_label
        ]

        # Tell them what we're doing and optionally ask if we should proceed
        if self.verbosity > 0 or self.interactive:
            self.stdout.write(
                self.style.MIGRATE_HEADING(
                    "Will squash the following migrations:"))
            for migration in migrations_to_squash:
                self.stdout.write(" - %s" % migration.name)

            if self.interactive:
                answer = None
                while not answer or answer not in "yn":
                    answer = six.moves.input("Do you wish to proceed? [yN] ")
                    if not answer:
                        answer = "n"
                        break
                    else:
                        answer = answer[0].lower()
                if answer != "y":
                    return

        # Load the operations from all those migrations and concat together,
        # along with collecting external dependencies and detecting
        # double-squashing
        operations = []
        dependencies = set()
        for smigration in migrations_to_squash:
            if smigration.replaces:
                raise CommandError(
                    "You cannot squash squashed migrations! Please transition it to a normal migration first: https://docs.djangoproject.com/en/1.7/topics/migrations/#squashing-migrations"
                )
            operations.extend(smigration.operations)
            for dependency in smigration.dependencies:
                if isinstance(dependency, SwappableTuple):
                    if settings.AUTH_USER_MODEL == dependency.setting:
                        dependencies.add(("__setting__", "AUTH_USER_MODEL"))
                    else:
                        dependencies.add(dependency)
                elif dependency[0] != smigration.app_label:
                    dependencies.add(dependency)

        if self.verbosity > 0:
            self.stdout.write(self.style.MIGRATE_HEADING("Optimizing..."))

        optimizer = MigrationOptimizer()
        new_operations = optimizer.optimize(operations, migration.app_label)

        if self.verbosity > 0:
            if len(new_operations) == len(operations):
                self.stdout.write("  No optimizations possible.")
            else:
                self.stdout.write(
                    "  Optimized from %s operations to %s operations." %
                    (len(operations), len(new_operations)))

        # Work out the value of replaces (any squashed ones we're re-squashing)
        # need to feed their replaces into ours
        replaces = []
        for migration in migrations_to_squash:
            if migration.replaces:
                replaces.extend(migration.replaces)
            else:
                replaces.append((migration.app_label, migration.name))

        # Make a new migration with those operations
        subclass = type(
            "Migration", (migrations.Migration, ), {
                "dependencies": dependencies,
                "operations": new_operations,
                "replaces": replaces,
            })
        new_migration = subclass("0001_squashed_%s" % migration.name,
                                 app_label)

        # Write out the new migration file
        writer = MigrationWriter(new_migration)
        with open(writer.path, "wb") as fh:
            fh.write(writer.as_string())

        if self.verbosity > 0:
            self.stdout.write(
                self.style.MIGRATE_HEADING(
                    "Created new squashed migration %s" % writer.path))
            self.stdout.write(
                "  You should commit this migration but leave the old ones in place;"
            )
            self.stdout.write(
                "  the new migration will be used for new installs. Once you are sure"
            )
            self.stdout.write(
                "  all instances of the codebase have applied the migrations you squashed,"
            )
            self.stdout.write("  you can delete them.")
            if writer.needs_manual_porting:
                self.stdout.write(
                    self.style.MIGRATE_HEADING("Manual porting required"))
                self.stdout.write(
                    "  Your migrations contained functions that must be manually copied over,"
                )
                self.stdout.write(
                    "  as we could not safely copy their implementation.")
                self.stdout.write(
                    "  See the comment at the top of the squashed migration for details."
                )
Example #54
0
    def handle(self, app_label=None, migration_name=None, **options):

        self.verbosity = int(options.get('verbosity'))
        self.interactive = options.get('interactive')

        if app_label is None or migration_name is None:
            self.stderr.write(self.usage_str)
            sys.exit(1)

        # Load the current graph state, check the app and migration they asked for exists
        executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
        if app_label not in executor.loader.migrated_apps:
            raise CommandError(
                "App '%s' does not have migrations (so squashmigrations on it makes no sense)"
                % app_label)
        try:
            migration = executor.loader.get_migration_by_prefix(
                app_label, migration_name)
        except AmbiguityError:
            raise CommandError(
                "More than one migration matches '%s' in app '%s'. Please be more specific."
                % (migration_name, app_label))
        except KeyError:
            raise CommandError(
                "Cannot find a migration matching '%s' from app '%s'." %
                (migration_name, app_label))

        # Work out the list of predecessor migrations
        migrations_to_squash = [
            executor.loader.get_migration(al, mn)
            for al, mn in executor.loader.graph.forwards_plan((
                migration.app_label, migration.name))
            if al == migration.app_label
        ]

        # Tell them what we're doing and optionally ask if we should proceed
        if self.verbosity > 0 or self.interactive:
            self.stdout.write(
                self.style.MIGRATE_HEADING(
                    "Will squash the following migrations:"))
            for migration in migrations_to_squash:
                self.stdout.write(" - %s" % migration.name)

            if self.interactive:
                answer = None
                while not answer or answer not in "yn":
                    answer = six.moves.input("Do you wish to proceed? [yN] ")
                    if not answer:
                        answer = "n"
                        break
                    else:
                        answer = answer[0].lower()
                if answer != "y":
                    return

        # Load the operations from all those migrations and concat together
        operations = []
        for smigration in migrations_to_squash:
            operations.extend(smigration.operations)

        if self.verbosity > 0:
            self.stdout.write(self.style.MIGRATE_HEADING("Optimizing..."))

        optimizer = MigrationOptimizer()
        new_operations = optimizer.optimize(operations, migration.app_label)

        if self.verbosity > 0:
            if len(new_operations) == len(operations):
                self.stdout.write("  No optimizations possible.")
            else:
                self.stdout.write(
                    "  Optimized from %s operations to %s operations." %
                    (len(operations), len(new_operations)))

        # Work out the value of replaces (any squashed ones we're re-squashing)
        # need to feed their replaces into ours
        replaces = []
        for migration in migrations_to_squash:
            if migration.replaces:
                replaces.extend(migration.replaces)
            else:
                replaces.append((migration.app_label, migration.name))

        # Make a new migration with those operations
        subclass = type(
            "Migration", (migrations.Migration, ), {
                "dependencies": [],
                "operations": new_operations,
                "replaces": replaces,
            })
        new_migration = subclass("0001_squashed_%s" % migration.name,
                                 app_label)

        # Write out the new migration file
        writer = MigrationWriter(new_migration)
        with open(writer.path, "wb") as fh:
            fh.write(writer.as_string())

        if self.verbosity > 0:
            self.stdout.write(
                self.style.MIGRATE_HEADING(
                    "Created new squashed migration %s" % writer.path))
            self.stdout.write(
                "  You should commit this migration but leave the old ones in place;"
            )
            self.stdout.write(
                "  the new migration will be used for new installs. Once you are sure"
            )
            self.stdout.write(
                "  all instances of the codebase have applied the migrations you squashed,"
            )
            self.stdout.write("  you can delete them.")
Example #55
0
    def write_to_last_migration_files(self, changes):
        loader = MigrationLoader(connections[DEFAULT_DB_ALIAS])
        new_changes = {}
        update_previous_migration_paths = {}
        for app_label, app_migrations in changes.items():
            # Find last migration.
            leaf_migration_nodes = loader.graph.leaf_nodes(app=app_label)
            if len(leaf_migration_nodes) == 0:
                raise CommandError(
                    f"App {app_label} has no migration, cannot update last migration."
                )
            leaf_migration_node = leaf_migration_nodes[0]
            # Multiple leaf nodes have already been checked earlier in command.
            leaf_migration = loader.graph.nodes[leaf_migration_node]
            # Updated migration cannot be a squash migration, a dependency of
            # another migration, and cannot be already applied.
            if leaf_migration.replaces:
                raise CommandError(
                    f"Cannot update squash migration '{leaf_migration}'."
                )
            if leaf_migration_node in loader.applied_migrations:
                raise CommandError(
                    f"Cannot update applied migration '{leaf_migration}'."
                )
            depending_migrations = [
                migration
                for migration in loader.disk_migrations.values()
                if leaf_migration_node in migration.dependencies
            ]
            if depending_migrations:
                formatted_migrations = ", ".join(
                    [f"'{migration}'" for migration in depending_migrations]
                )
                raise CommandError(
                    f"Cannot update migration '{leaf_migration}' that migrations "
                    f"{formatted_migrations} depend on."
                )
            # Build new migration.
            for migration in app_migrations:
                leaf_migration.operations.extend(migration.operations)

                for dependency in migration.dependencies:
                    if isinstance(dependency, SwappableTuple):
                        if settings.AUTH_USER_MODEL == dependency.setting:
                            leaf_migration.dependencies.append(
                                ("__setting__", "AUTH_USER_MODEL")
                            )
                        else:
                            leaf_migration.dependencies.append(dependency)
                    elif dependency[0] != migration.app_label:
                        leaf_migration.dependencies.append(dependency)
            # Optimize migration.
            optimizer = MigrationOptimizer()
            leaf_migration.operations = optimizer.optimize(
                leaf_migration.operations, app_label
            )
            # Update name.
            previous_migration_path = MigrationWriter(leaf_migration).path
            suggested_name = (
                leaf_migration.name[:4] + "_" + leaf_migration.suggest_name()
            )
            if leaf_migration.name == suggested_name:
                new_name = leaf_migration.name + "_updated"
            else:
                new_name = suggested_name
            leaf_migration.name = new_name
            # Register overridden migration.
            new_changes[app_label] = [leaf_migration]
            update_previous_migration_paths[app_label] = previous_migration_path

        self.write_migration_files(new_changes, update_previous_migration_paths)
Example #56
0
    def test_serialize_enums(self):
        self.assertSerializedResultEqual(
            TextEnum.A,
            ("migrations.test_writer.TextEnum['A']", {"import migrations.test_writer"}),
        )
        self.assertSerializedResultEqual(
            TextTranslatedEnum.A,
            (
                "migrations.test_writer.TextTranslatedEnum['A']",
                {"import migrations.test_writer"},
            ),
        )
        self.assertSerializedResultEqual(
            BinaryEnum.A,
            (
                "migrations.test_writer.BinaryEnum['A']",
                {"import migrations.test_writer"},
            ),
        )
        self.assertSerializedResultEqual(
            IntEnum.B,
            ("migrations.test_writer.IntEnum['B']", {"import migrations.test_writer"}),
        )
        self.assertSerializedResultEqual(
            self.NestedEnum.A,
            (
                "migrations.test_writer.WriterTests.NestedEnum['A']",
                {"import migrations.test_writer"},
            ),
        )
        self.assertSerializedEqual(self.NestedEnum.A)

        field = models.CharField(
            default=TextEnum.B, choices=[(m.value, m) for m in TextEnum]
        )
        string = MigrationWriter.serialize(field)[0]
        self.assertEqual(
            string,
            "models.CharField(choices=["
            "('a-value', migrations.test_writer.TextEnum['A']), "
            "('value-b', migrations.test_writer.TextEnum['B'])], "
            "default=migrations.test_writer.TextEnum['B'])",
        )
        field = models.CharField(
            default=TextTranslatedEnum.A,
            choices=[(m.value, m) for m in TextTranslatedEnum],
        )
        string = MigrationWriter.serialize(field)[0]
        self.assertEqual(
            string,
            "models.CharField(choices=["
            "('a-value', migrations.test_writer.TextTranslatedEnum['A']), "
            "('value-b', migrations.test_writer.TextTranslatedEnum['B'])], "
            "default=migrations.test_writer.TextTranslatedEnum['A'])",
        )
        field = models.CharField(
            default=BinaryEnum.B, choices=[(m.value, m) for m in BinaryEnum]
        )
        string = MigrationWriter.serialize(field)[0]
        self.assertEqual(
            string,
            "models.CharField(choices=["
            "(b'a-value', migrations.test_writer.BinaryEnum['A']), "
            "(b'value-b', migrations.test_writer.BinaryEnum['B'])], "
            "default=migrations.test_writer.BinaryEnum['B'])",
        )
        field = models.IntegerField(
            default=IntEnum.A, choices=[(m.value, m) for m in IntEnum]
        )
        string = MigrationWriter.serialize(field)[0]
        self.assertEqual(
            string,
            "models.IntegerField(choices=["
            "(1, migrations.test_writer.IntEnum['A']), "
            "(2, migrations.test_writer.IntEnum['B'])], "
            "default=migrations.test_writer.IntEnum['A'])",
        )
Example #57
0
 def write_migration_files(self, changes, update_previous_migration_paths=None):
     """
     Take a changes dict and write them out as migration files.
     """
     directory_created = {}
     for app_label, app_migrations in changes.items():
         if self.verbosity >= 1:
             self.log(self.style.MIGRATE_HEADING("Migrations for '%s':" % app_label))
         for migration in app_migrations:
             # Describe the migration
             writer = MigrationWriter(migration, self.include_header)
             if self.verbosity >= 1:
                 # Display a relative path if it's below the current working
                 # directory, or an absolute path otherwise.
                 migration_string = self.get_relative_path(writer.path)
                 self.log("  %s\n" % self.style.MIGRATE_LABEL(migration_string))
                 for operation in migration.operations:
                     self.log("    - %s" % operation.describe())
                 if self.scriptable:
                     self.stdout.write(migration_string)
             if not self.dry_run:
                 # Write the migrations file to the disk.
                 migrations_directory = os.path.dirname(writer.path)
                 if not directory_created.get(app_label):
                     os.makedirs(migrations_directory, exist_ok=True)
                     init_path = os.path.join(migrations_directory, "__init__.py")
                     if not os.path.isfile(init_path):
                         open(init_path, "w").close()
                     # We just do this once per app
                     directory_created[app_label] = True
                 migration_string = writer.as_string()
                 with open(writer.path, "w", encoding="utf-8") as fh:
                     fh.write(migration_string)
                     self.written_files.append(writer.path)
                 if update_previous_migration_paths:
                     prev_path = update_previous_migration_paths[app_label]
                     rel_prev_path = self.get_relative_path(prev_path)
                     if writer.needs_manual_porting:
                         migration_path = self.get_relative_path(writer.path)
                         self.log(
                             self.style.WARNING(
                                 f"Updated migration {migration_path} requires "
                                 f"manual porting.\n"
                                 f"Previous migration {rel_prev_path} was kept and "
                                 f"must be deleted after porting functions manually."
                             )
                         )
                     else:
                         os.remove(prev_path)
                         self.log(f"Deleted {rel_prev_path}")
             elif self.verbosity == 3:
                 # Alternatively, makemigrations --dry-run --verbosity 3
                 # will log the migrations rather than saving the file to
                 # the disk.
                 self.log(
                     self.style.MIGRATE_HEADING(
                         "Full migrations file '%s':" % writer.filename
                     )
                 )
                 self.log(writer.as_string())
     run_formatters(self.written_files)
Example #58
0
                                        "false").lower() == "true")

# Rendre certains warnings silencieux
SILENCED_SYSTEM_CHECKS = [
    # On a remplacé django.contrib.auth.context_processors.auth par un équivalent, agir.authentication.context_processors.auth
    "admin.E402",
    # social_django utilise encore des champs postgres JSON (au lieu du nouveau JSONField de Django)
    "fields.W904",
]

# Django ne sait pas par défaut sérializer les types intervalle de psycopg2
# Cela fait du coup planter `makemigrate` quand il tente de séralizer les valeurs
# par défaut des champs de dates pour les modèles de mandats (dans l'appli élus).
# Il faut enregistrer manuellement le serializer correspondant auprès du générateur
# de migrations.
MigrationWriter.register_serializer(DateRange, RangeSerializer)

# Django < 3.1 not compatible with GDAL 3
if os.environ.get("GDAL_LIBRARY_PATH"):
    GDAL_LIBRARY_PATH = os.environ.get("GDAL_LIBRARY_PATH")

ENABLE_API = os.environ.get("ENABLE_API", "n").lower() in YES_VALUES or DEBUG
ENABLE_ADMIN = os.environ.get("ENABLE_ADMIN",
                              "n").lower() in YES_VALUES or DEBUG
ENABLE_FRONT = os.environ.get("ENABLE_FRONT",
                              "n").lower() in YES_VALUES or DEBUG

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

TEST_RUNNER = "agir.api.test_runner.TestRunner"
Example #59
0
 def serialize_round_trip(self, value):
     string, imports = MigrationWriter.serialize(value)
     return self.safe_exec(
         "%s\ntest_value_result = %s" % ("\n".join(imports), string),
         value)['test_value_result']
Example #60
0
 def handle_merge(self, loader, conflicts):
     """
     Handles merging together conflicted migrations interactively,
     if it's safe; otherwise, advises on how to fix it.
     """
     if self.interactive:
         questioner = InteractiveMigrationQuestioner()
     else:
         questioner = MigrationQuestioner()
     for app_label, migration_names in conflicts.items():
         # Grab out the migrations in question, and work out their
         # common ancestor.
         merge_migrations = []
         for migration_name in migration_names:
             migration = loader.get_migration(app_label, migration_name)
             migration.ancestry = loader.graph.forwards_plan(
                 (app_label, migration_name))
             merge_migrations.append(migration)
         common_ancestor = None
         for level in zip(*[m.ancestry for m in merge_migrations]):
             if reduce(operator.eq, level):
                 common_ancestor = level[0]
             else:
                 break
         if common_ancestor is None:
             raise ValueError("Could not find common ancestor of %s" %
                              migration_names)
         # Now work out the operations along each divergent branch
         for migration in merge_migrations:
             migration.branch = migration.ancestry[(
                 migration.ancestry.index(common_ancestor) + 1):]
             migration.merged_operations = []
             for node_app, node_name in migration.branch:
                 migration.merged_operations.extend(
                     loader.get_migration(node_app, node_name).operations)
         # In future, this could use some of the Optimizer code
         # (can_optimize_through) to automatically see if they're
         # mergeable. For now, we always just prompt the user.
         if self.verbosity > 0:
             self.stdout.write(
                 self.style.MIGRATE_HEADING("Merging %s" % app_label))
             for migration in merge_migrations:
                 self.stdout.write(
                     self.style.MIGRATE_LABEL("  Branch %s" %
                                              migration.name))
                 for operation in migration.merged_operations:
                     self.stdout.write("    - %s\n" % operation.describe())
         if questioner.ask_merge(app_label):
             # If they still want to merge it, then write out an empty
             # file depending on the migrations needing merging.
             numbers = [
                 MigrationAutodetector.parse_number(migration.name)
                 for migration in merge_migrations
             ]
             try:
                 biggest_number = max([x for x in numbers if x is not None])
             except ValueError:
                 biggest_number = 1
             subclass = type(
                 "Migration", (migrations.Migration, ), {
                     "dependencies": [(app_label, migration.name)
                                      for migration in merge_migrations],
                 })
             new_migration = subclass("%04i_merge" % (biggest_number + 1),
                                      app_label)
             writer = MigrationWriter(new_migration)
             with open(writer.path, "wb") as fh:
                 fh.write(writer.as_string())
             if self.verbosity > 0:
                 self.stdout.write("\nCreated new merge migration %s" %
                                   writer.path)