summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorAndrei Kulakov <andrei.avk@gmail.com>2015-03-31 16:30:39 -0400
committerTim Graham <timograham@gmail.com>2015-07-13 15:57:40 -0400
commitdb97a8849519a3933bf4abd2184efd68ebc21965 (patch)
tree456edbf87bf6084e3345723b96492c7cd48a6a3a /django
parenta2b999dfcac9bc92513a36ec6b3033ded1561c66 (diff)
Fixed #24375 -- Added Migration.initial attribute
The new attribute is checked when the `migrate --fake-initial` option is used. initial will be set to True for all initial migrations (this is particularly useful when initial migrations are split) as well as for squashed migrations.
Diffstat (limited to 'django')
-rw-r--r--django/core/management/commands/squashmigrations.py1
-rw-r--r--django/db/migrations/autodetector.py2
-rw-r--r--django/db/migrations/executor.py41
-rw-r--r--django/db/migrations/migration.py7
-rw-r--r--django/db/migrations/writer.py6
5 files changed, 47 insertions, 10 deletions
diff --git a/django/core/management/commands/squashmigrations.py b/django/core/management/commands/squashmigrations.py
index 74e5ce78c3..db55195187 100644
--- a/django/core/management/commands/squashmigrations.py
+++ b/django/core/management/commands/squashmigrations.py
@@ -131,6 +131,7 @@ class Command(BaseCommand):
"dependencies": dependencies,
"operations": new_operations,
"replaces": replaces,
+ "initial": True,
})
new_migration = subclass("0001_squashed_%s" % migration.name, app_label)
diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py
index ddf8e36b83..b20946999f 100644
--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -33,6 +33,7 @@ class MigrationAutodetector(object):
self.from_state = from_state
self.to_state = to_state
self.questioner = questioner or MigrationQuestioner()
+ self.existing_apps = {app for app, model in from_state.models}
def changes(self, graph, trim_to_apps=None, convert_apps=None, migration_name=None):
"""
@@ -297,6 +298,7 @@ class MigrationAutodetector(object):
instance = subclass("auto_%i" % (len(self.migrations.get(app_label, [])) + 1), app_label)
instance.dependencies = list(dependencies)
instance.operations = chopped
+ instance.initial = app_label not in self.existing_apps
self.migrations.setdefault(app_label, []).append(instance)
chop_mode = False
else:
diff --git a/django/db/migrations/executor.py b/django/db/migrations/executor.py
index aeb021e33c..c25aee6193 100644
--- a/django/db/migrations/executor.py
+++ b/django/db/migrations/executor.py
@@ -197,19 +197,25 @@ class MigrationExecutor(object):
def detect_soft_applied(self, project_state, migration):
"""
Tests whether a migration has been implicitly applied - that the
- tables it would create exist. This is intended only for use
- on initial migrations (as it only looks for CreateModel).
+ tables or columns it would create exist. This is intended only for use
+ on initial migrations (as it only looks for CreateModel and AddField).
"""
- # Bail if the migration isn't the first one in its app
- if [name for app, name in migration.dependencies if app == migration.app_label]:
+ if migration.initial is None:
+ # Bail if the migration isn't the first one in its app
+ if any(app == migration.app_label for app, name in migration.dependencies):
+ return False, project_state
+ elif migration.initial is False:
+ # Bail if it's NOT an initial migration
return False, project_state
+
if project_state is None:
after_state = self.loader.project_state((migration.app_label, migration.name), at_end=True)
else:
after_state = migration.mutate_state(project_state)
apps = after_state.apps
- found_create_migration = False
- # Make sure all create model are done
+ found_create_model_migration = False
+ found_add_field_migration = False
+ # Make sure all create model and add field operations are done
for operation in migration.operations:
if isinstance(operation, migrations.CreateModel):
model = apps.get_model(migration.app_label, operation.name)
@@ -217,9 +223,26 @@ class MigrationExecutor(object):
# We have to fetch the model to test with from the
# main app cache, as it's not a direct dependency.
model = global_apps.get_model(model._meta.swapped)
+ if model._meta.proxy or not model._meta.managed:
+ continue
if model._meta.db_table not in self.connection.introspection.table_names(self.connection.cursor()):
return False, project_state
- found_create_migration = True
- # If we get this far and we found at least one CreateModel migration,
+ found_create_model_migration = True
+ elif isinstance(operation, migrations.AddField):
+ model = apps.get_model(migration.app_label, operation.model_name)
+ if model._meta.swapped:
+ # We have to fetch the model to test with from the
+ # main app cache, as it's not a direct dependency.
+ model = global_apps.get_model(model._meta.swapped)
+ if model._meta.proxy or not model._meta.managed:
+ continue
+
+ table = model._meta.db_table
+ db_field = model._meta.get_field(operation.name).column
+ fields = self.connection.introspection.get_table_description(self.connection.cursor(), table)
+ if db_field not in (f.name for f in fields):
+ return False, project_state
+ found_add_field_migration = True
+ # If we get this far and we found at least one CreateModel or AddField migration,
# the migration is considered implicitly applied.
- return found_create_migration, after_state
+ return (found_create_model_migration or found_add_field_migration), after_state
diff --git a/django/db/migrations/migration.py b/django/db/migrations/migration.py
index 59edf599f6..7641eb45bc 100644
--- a/django/db/migrations/migration.py
+++ b/django/db/migrations/migration.py
@@ -41,6 +41,13 @@ class Migration(object):
# are not applied.
replaces = []
+ # Is this an initial migration? Initial migrations are skipped on
+ # --fake-initial if the table or fields already exist. If None, check if
+ # the migration has any dependencies to determine if there are dependencies
+ # to tell if db introspection needs to be done. If True, always perform
+ # introspection. If False, never perform introspection.
+ initial = None
+
def __init__(self, name, app_label):
self.name = name
self.app_label = app_label
diff --git a/django/db/migrations/writer.py b/django/db/migrations/writer.py
index a7848d744f..11ecb6ed2b 100644
--- a/django/db/migrations/writer.py
+++ b/django/db/migrations/writer.py
@@ -155,6 +155,7 @@ class MigrationWriter(object):
"""
items = {
"replaces_str": "",
+ "initial_str": "",
}
imports = set()
@@ -211,6 +212,9 @@ class MigrationWriter(object):
if self.migration.replaces:
items['replaces_str'] = "\n replaces = %s\n" % self.serialize(self.migration.replaces)[0]
+ if self.migration.initial:
+ items['initial_str'] = "\n initial = True\n"
+
return (MIGRATION_TEMPLATE % items).encode("utf8")
@staticmethod
@@ -508,7 +512,7 @@ from __future__ import unicode_literals
%(imports)s
class Migration(migrations.Migration):
-%(replaces_str)s
+%(replaces_str)s%(initial_str)s
dependencies = [
%(dependencies)s\
]