Fixed #24755 -- Hid operations from dependency apps when merging migrations

Thanks Carl Meyer for the report and Tim Graham for the review.
This commit is contained in:
Markus Holtermann 2015-08-09 23:53:09 +10:00
parent 75b5de8d5f
commit 0271a11ba5
7 changed files with 109 additions and 1 deletions

View File

@ -194,13 +194,17 @@ class Command(BaseCommand):
questioner = InteractiveMigrationQuestioner() questioner = InteractiveMigrationQuestioner()
else: else:
questioner = MigrationQuestioner(defaults={'ask_merge': True}) questioner = MigrationQuestioner(defaults={'ask_merge': True})
for app_label, migration_names in conflicts.items(): for app_label, migration_names in conflicts.items():
# Grab out the migrations in question, and work out their # Grab out the migrations in question, and work out their
# common ancestor. # common ancestor.
merge_migrations = [] merge_migrations = []
for migration_name in migration_names: for migration_name in migration_names:
migration = loader.get_migration(app_label, migration_name) migration = loader.get_migration(app_label, migration_name)
migration.ancestry = loader.graph.forwards_plan((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) merge_migrations.append(migration)
all_items_equal = lambda seq: all(item == seq[0] for item in seq[1:]) 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]) merge_migrations_generations = zip(*[m.ancestry for m in merge_migrations])

View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
"Author",
[
("id", models.AutoField(primary_key=True)),
("name", models.CharField(max_length=255)),
("slug", models.SlugField(null=True)),
("age", models.IntegerField(default=0)),
("silly_field", models.BooleanField(default=False)),
],
),
migrations.CreateModel(
"Tribble",
[
("id", models.AutoField(primary_key=True)),
("fluffy", models.BooleanField(default=True)),
],
)
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("conflicting_app_with_dependencies", "0001_initial"),
]
operations = [
migrations.CreateModel(
"Something",
[
("id", models.AutoField(primary_key=True)),
],
)
]

View File

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("conflicting_app_with_dependencies", "0001_initial"),
("migrated_app", "0001_initial"),
]
operations = [
migrations.DeleteModel("Tribble"),
migrations.RemoveField("Author", "silly_field"),
migrations.AddField("Author", "rating", models.IntegerField(default=0)),
migrations.CreateModel(
"Book",
[
("id", models.AutoField(primary_key=True)),
],
)
]

View File

@ -920,6 +920,39 @@ class MakeMigrationsTests(MigrationTestBase):
except CommandError: except CommandError:
self.fail("Makemigrations fails resolving conflicts in an unspecified app") self.fail("Makemigrations fails resolving conflicts in an unspecified app")
@override_settings(
INSTALLED_APPS=[
"migrations.migrations_test_apps.migrated_app",
"migrations.migrations_test_apps.conflicting_app_with_dependencies"])
def test_makemigrations_merge_dont_output_dependency_operations(self):
"""
Makes sure that makemigrations --merge does not output any operations
from apps that don't belong to a given app.
"""
# Monkeypatch interactive questioner to auto accept
with mock.patch('django.db.migrations.questioner.input', mock.Mock(return_value='N')):
out = six.StringIO()
with mock.patch('django.core.management.color.supports_color', lambda *args: False):
call_command(
"makemigrations", "conflicting_app_with_dependencies",
merge=True, interactive=True, stdout=out
)
val = out.getvalue().lower()
self.assertIn('merging conflicting_app_with_dependencies\n', val)
self.assertIn(
' branch 0002_conflicting_second\n'
' - create model something\n',
val
)
self.assertIn(
' branch 0002_second\n'
' - delete model tribble\n'
' - remove field silly_field from author\n'
' - add field rating to author\n'
' - create model book\n',
val
)
def test_makemigrations_with_custom_name(self): def test_makemigrations_with_custom_name(self):
""" """
Makes sure that makemigrations generate a custom migration. Makes sure that makemigrations generate a custom migration.