mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
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.
This commit is contained in:
committed by
Tim Graham
parent
a2b999dfca
commit
db97a88495
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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\
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user