Loading django/db/migrations/operations/__init__.py +2 −2 Original line number Diff line number Diff line from .models import (CreateModel, DeleteModel, AlterModelTable, AlterUniqueTogether, AlterIndexTogether) AlterUniqueTogether, AlterIndexTogether, RenameModel) from .fields import AddField, RemoveField, AlterField, RenameField from .special import SeparateDatabaseAndState, RunSQL, RunPython __all__ = [ 'CreateModel', 'DeleteModel', 'AlterModelTable', 'AlterUniqueTogether', 'AlterIndexTogether', 'RenameModel', 'AlterIndexTogether', 'AddField', 'RemoveField', 'AlterField', 'RenameField', 'SeparateDatabaseAndState', 'RunSQL', 'RunPython', ] django/db/migrations/operations/base.py +12 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,9 @@ class Operation(object): Note that some operations won't modify memory state at all (e.g. data copying operations), and some will need their modifications to be optionally specified by the user (e.g. custom Python code snippets) Due to the way this class deals with deconstruction, it should be considered immutable. """ # If this migration can be run in reverse. Loading Loading @@ -76,6 +79,15 @@ class Operation(object): """ return True def references_field(self, model_name, name, app_label=None): """ Returns True if there is a chance this operation references the given field name, with an optional app label for accuracy. Used for optimization. If in doubt, return True. """ return self.references_model(model_name, app_label) def __repr__(self): return "<%s %s%s>" % ( self.__class__.__name__, Loading django/db/migrations/operations/fields.py +33 −6 Original line number Diff line number Diff line Loading @@ -8,7 +8,7 @@ class AddField(Operation): """ def __init__(self, model_name, name, field): self.model_name = model_name.lower() self.model_name = model_name self.name = name self.field = field Loading @@ -33,10 +33,16 @@ class AddField(Operation): return ( (self.__class__ == other.__class__) and (self.name == other.name) and (self.model_name == other.model_name) and (self.model_name.lower() == other.model_name.lower()) and (self.field.deconstruct()[1:] == other.field.deconstruct()[1:]) ) def references_model(self, name, app_label=None): return name.lower() == self.model_name.lower() def references_field(self, model_name, name, app_label=None): return self.references_model(model_name) and name.lower() == self.name.lower() class RemoveField(Operation): """ Loading @@ -44,7 +50,7 @@ class RemoveField(Operation): """ def __init__(self, model_name, name): self.model_name = model_name.lower() self.model_name = model_name self.name = name def state_forwards(self, app_label, state): Loading @@ -68,6 +74,12 @@ class RemoveField(Operation): def describe(self): return "Remove field %s from %s" % (self.name, self.model_name) def references_model(self, name, app_label=None): return name.lower() == self.model_name.lower() def references_field(self, model_name, name, app_label=None): return self.references_model(model_name) and name.lower() == self.name.lower() class AlterField(Operation): """ Loading @@ -75,7 +87,7 @@ class AlterField(Operation): """ def __init__(self, model_name, name, field): self.model_name = model_name.lower() self.model_name = model_name self.name = name self.field = field Loading Loading @@ -104,10 +116,16 @@ class AlterField(Operation): return ( (self.__class__ == other.__class__) and (self.name == other.name) and (self.model_name == other.model_name) and (self.model_name.lower() == other.model_name.lower()) and (self.field.deconstruct()[1:] == other.field.deconstruct()[1:]) ) def references_model(self, name, app_label=None): return name.lower() == self.model_name.lower() def references_field(self, model_name, name, app_label=None): return self.references_model(model_name) and name.lower() == self.name.lower() class RenameField(Operation): """ Loading @@ -115,7 +133,7 @@ class RenameField(Operation): """ def __init__(self, model_name, old_name, new_name): self.model_name = model_name.lower() self.model_name = model_name self.old_name = old_name self.new_name = new_name Loading Loading @@ -146,3 +164,12 @@ class RenameField(Operation): def describe(self): return "Rename field %s on %s to %s" % (self.old_name, self.model_name, self.new_name) def references_model(self, name, app_label=None): return name.lower() == self.model_name.lower() def references_field(self, model_name, name, app_label=None): return self.references_model(model_name) and ( name.lower() == self.old_name.lower() or name.lower() == self.new_name.lower() ) django/db/migrations/operations/models.py +48 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,54 @@ class DeleteModel(Operation): return "Delete model %s" % (self.name, ) class RenameModel(Operation): """ Renames a model. """ def __init__(self, old_name, new_name): self.old_name = old_name self.new_name = new_name def state_forwards(self, app_label, state): state.models[app_label, self.new_name.lower()] = state.models[app_label, self.old_name.lower()] state.models[app_label, self.new_name.lower()].name = self.new_name del state.models[app_label, self.old_name.lower()] def database_forwards(self, app_label, schema_editor, from_state, to_state): old_app_cache = from_state.render() new_app_cache = to_state.render() old_model = old_app_cache.get_model(app_label, self.old_name) new_model = new_app_cache.get_model(app_label, self.new_name) if router.allow_migrate(schema_editor.connection.alias, new_model): schema_editor.alter_db_table( new_model, old_model._meta.db_table, new_model._meta.db_table, ) def database_backwards(self, app_label, schema_editor, from_state, to_state): old_app_cache = from_state.render() new_app_cache = to_state.render() old_model = old_app_cache.get_model(app_label, self.new_name) new_model = new_app_cache.get_model(app_label, self.old_name) if router.allow_migrate(schema_editor.connection.alias, new_model): schema_editor.alter_db_table( new_model, old_model._meta.db_table, new_model._meta.db_table, ) def references_model(self, name, app_label=None): return ( name.lower() == self.old_name.lower() or name.lower() == self.new_name.lower() ) def describe(self): return "Rename model %s to %s" % (self.old_name, self.new_name) class AlterModelTable(Operation): """ Renames a model's table Loading django/db/migrations/optimizer.py +207 −10 Original line number Diff line number Diff line Loading @@ -71,10 +71,86 @@ class MigrationOptimizer(object): or None, meaning this pair cannot be optimized. """ submethods = [ (migrations.CreateModel, migrations.DeleteModel, self.reduce_model_create_delete), (migrations.AlterModelTable, migrations.DeleteModel, self.reduce_model_alter_delete), (migrations.AlterUniqueTogether, migrations.DeleteModel, self.reduce_model_alter_delete), (migrations.AlterIndexTogether, migrations.DeleteModel, self.reduce_model_alter_delete), ( migrations.CreateModel, migrations.DeleteModel, self.reduce_model_create_delete, ), ( migrations.AlterModelTable, migrations.DeleteModel, self.reduce_model_alter_delete, ), ( migrations.AlterUniqueTogether, migrations.DeleteModel, self.reduce_model_alter_delete, ), ( migrations.AlterIndexTogether, migrations.DeleteModel, self.reduce_model_alter_delete, ), ( migrations.CreateModel, migrations.RenameModel, self.reduce_model_create_rename, ), ( migrations.RenameModel, migrations.RenameModel, self.reduce_model_rename_self, ), ( migrations.CreateModel, migrations.AddField, self.reduce_create_model_add_field, ), ( migrations.CreateModel, migrations.AlterField, self.reduce_create_model_alter_field, ), ( migrations.CreateModel, migrations.RemoveField, self.reduce_create_model_remove_field, ), ( migrations.AddField, migrations.AlterField, self.reduce_add_field_alter_field, ), ( migrations.AddField, migrations.RemoveField, self.reduce_add_field_delete_field, ), ( migrations.AlterField, migrations.RemoveField, self.reduce_alter_field_delete_field, ), ( migrations.AddField, migrations.RenameField, self.reduce_add_field_rename_field, ), ( migrations.AlterField, migrations.RenameField, self.reduce_alter_field_rename_field, ), ( migrations.CreateModel, migrations.RenameField, self.reduce_create_model_rename_field, ), ( migrations.RenameField, migrations.RenameField, self.reduce_rename_field_self, ), ] for ia, ib, om in submethods: if isinstance(operation, ia) and isinstance(other, ib): Loading @@ -85,17 +161,130 @@ class MigrationOptimizer(object): """ Folds a CreateModel and a DeleteModel into nothing. """ if operation.name == other.name: if operation.name.lower() == other.name.lower(): return [] return None def reduce_model_alter_delete(self, operation, other): """ Folds an AlterModelSomething and a DeleteModel into nothing. Folds an AlterModelSomething and a DeleteModel into just delete. """ if operation.name == other.name: if operation.name.lower() == other.name.lower(): return [other] return None def reduce_model_create_rename(self, operation, other): """ Folds a model rename into its create """ if operation.name.lower() == other.old_name.lower(): return [migrations.CreateModel( other.new_name, fields = operation.fields, options = operation.options, bases = operation.bases, )] def reduce_model_rename_self(self, operation, other): """ Folds a model rename into another one """ if operation.new_name.lower() == other.old_name.lower(): return [ migrations.RenameModel( operation.old_name, other.new_name, ) ] def reduce_create_model_add_field(self, operation, other): if operation.name.lower() == other.model_name.lower(): return [migrations.CreateModel( operation.name, fields = operation.fields + [(other.name, other.field)], options = operation.options, bases = operation.bases, )] def reduce_create_model_alter_field(self, operation, other): if operation.name.lower() == other.model_name.lower(): return [migrations.CreateModel( operation.name, fields = [ (n, other.field if n == other.name else v) for n, v in operation.fields ], options = operation.options, bases = operation.bases, )] def reduce_create_model_rename_field(self, operation, other): if operation.name.lower() == other.model_name.lower(): return [migrations.CreateModel( operation.name, fields = [ (other.new_name if n == other.old_name else n, v) for n, v in operation.fields ], options = operation.options, bases = operation.bases, )] def reduce_create_model_remove_field(self, operation, other): if operation.name.lower() == other.model_name.lower(): return [migrations.CreateModel( operation.name, fields = [ (n, v) for n, v in operation.fields if n.lower() != other.name.lower() ], options = operation.options, bases = operation.bases, )] def reduce_add_field_alter_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.name.lower(): return [migrations.AddField( model_name = operation.model_name, name = operation.name, field = other.field, )] def reduce_add_field_delete_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.name.lower(): return [] def reduce_alter_field_delete_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.name.lower(): return [other] def reduce_add_field_rename_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.old_name.lower(): return [migrations.AddField( model_name = operation.model_name, name = other.new_name, field = operation.field, )] def reduce_alter_field_rename_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.old_name.lower(): return [ other, migrations.AlterField( model_name = operation.model_name, name = other.new_name, field = operation.field, ), ] def reduce_rename_field_self(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.new_name.lower() == other.old_name.lower(): return [ migrations.RenameField( operation.model_name, operation.old_name, other.new_name, ), ] #### THROUGH CHECKS #### Loading @@ -107,14 +296,22 @@ class MigrationOptimizer(object): """ MODEL_LEVEL_OPERATIONS = ( migrations.CreateModel, migrations.DeleteModel, migrations.AlterModelTable, migrations.AlterUniqueTogether, migrations.AlterIndexTogether, ) FIELD_LEVEL_OPERATIONS = ( migrations.AddField, migrations.AlterField, ) # If it's a model level operation, let it through if there's # nothing that looks like a reference to us in 'other'. if isinstance(operation, MODEL_LEVEL_OPERATIONS): if not other.references_model(operation.name, app_label): return True # If it's field level, only let it through things that don't reference # the field (which includes not referencing the model) if isinstance(operation, FIELD_LEVEL_OPERATIONS): if not other.references_field(operation.model_name, operation.name, app_label): return True return False Loading
django/db/migrations/operations/__init__.py +2 −2 Original line number Diff line number Diff line from .models import (CreateModel, DeleteModel, AlterModelTable, AlterUniqueTogether, AlterIndexTogether) AlterUniqueTogether, AlterIndexTogether, RenameModel) from .fields import AddField, RemoveField, AlterField, RenameField from .special import SeparateDatabaseAndState, RunSQL, RunPython __all__ = [ 'CreateModel', 'DeleteModel', 'AlterModelTable', 'AlterUniqueTogether', 'AlterIndexTogether', 'RenameModel', 'AlterIndexTogether', 'AddField', 'RemoveField', 'AlterField', 'RenameField', 'SeparateDatabaseAndState', 'RunSQL', 'RunPython', ]
django/db/migrations/operations/base.py +12 −0 Original line number Diff line number Diff line Loading @@ -9,6 +9,9 @@ class Operation(object): Note that some operations won't modify memory state at all (e.g. data copying operations), and some will need their modifications to be optionally specified by the user (e.g. custom Python code snippets) Due to the way this class deals with deconstruction, it should be considered immutable. """ # If this migration can be run in reverse. Loading Loading @@ -76,6 +79,15 @@ class Operation(object): """ return True def references_field(self, model_name, name, app_label=None): """ Returns True if there is a chance this operation references the given field name, with an optional app label for accuracy. Used for optimization. If in doubt, return True. """ return self.references_model(model_name, app_label) def __repr__(self): return "<%s %s%s>" % ( self.__class__.__name__, Loading
django/db/migrations/operations/fields.py +33 −6 Original line number Diff line number Diff line Loading @@ -8,7 +8,7 @@ class AddField(Operation): """ def __init__(self, model_name, name, field): self.model_name = model_name.lower() self.model_name = model_name self.name = name self.field = field Loading @@ -33,10 +33,16 @@ class AddField(Operation): return ( (self.__class__ == other.__class__) and (self.name == other.name) and (self.model_name == other.model_name) and (self.model_name.lower() == other.model_name.lower()) and (self.field.deconstruct()[1:] == other.field.deconstruct()[1:]) ) def references_model(self, name, app_label=None): return name.lower() == self.model_name.lower() def references_field(self, model_name, name, app_label=None): return self.references_model(model_name) and name.lower() == self.name.lower() class RemoveField(Operation): """ Loading @@ -44,7 +50,7 @@ class RemoveField(Operation): """ def __init__(self, model_name, name): self.model_name = model_name.lower() self.model_name = model_name self.name = name def state_forwards(self, app_label, state): Loading @@ -68,6 +74,12 @@ class RemoveField(Operation): def describe(self): return "Remove field %s from %s" % (self.name, self.model_name) def references_model(self, name, app_label=None): return name.lower() == self.model_name.lower() def references_field(self, model_name, name, app_label=None): return self.references_model(model_name) and name.lower() == self.name.lower() class AlterField(Operation): """ Loading @@ -75,7 +87,7 @@ class AlterField(Operation): """ def __init__(self, model_name, name, field): self.model_name = model_name.lower() self.model_name = model_name self.name = name self.field = field Loading Loading @@ -104,10 +116,16 @@ class AlterField(Operation): return ( (self.__class__ == other.__class__) and (self.name == other.name) and (self.model_name == other.model_name) and (self.model_name.lower() == other.model_name.lower()) and (self.field.deconstruct()[1:] == other.field.deconstruct()[1:]) ) def references_model(self, name, app_label=None): return name.lower() == self.model_name.lower() def references_field(self, model_name, name, app_label=None): return self.references_model(model_name) and name.lower() == self.name.lower() class RenameField(Operation): """ Loading @@ -115,7 +133,7 @@ class RenameField(Operation): """ def __init__(self, model_name, old_name, new_name): self.model_name = model_name.lower() self.model_name = model_name self.old_name = old_name self.new_name = new_name Loading Loading @@ -146,3 +164,12 @@ class RenameField(Operation): def describe(self): return "Rename field %s on %s to %s" % (self.old_name, self.model_name, self.new_name) def references_model(self, name, app_label=None): return name.lower() == self.model_name.lower() def references_field(self, model_name, name, app_label=None): return self.references_model(model_name) and ( name.lower() == self.old_name.lower() or name.lower() == self.new_name.lower() )
django/db/migrations/operations/models.py +48 −0 Original line number Diff line number Diff line Loading @@ -91,6 +91,54 @@ class DeleteModel(Operation): return "Delete model %s" % (self.name, ) class RenameModel(Operation): """ Renames a model. """ def __init__(self, old_name, new_name): self.old_name = old_name self.new_name = new_name def state_forwards(self, app_label, state): state.models[app_label, self.new_name.lower()] = state.models[app_label, self.old_name.lower()] state.models[app_label, self.new_name.lower()].name = self.new_name del state.models[app_label, self.old_name.lower()] def database_forwards(self, app_label, schema_editor, from_state, to_state): old_app_cache = from_state.render() new_app_cache = to_state.render() old_model = old_app_cache.get_model(app_label, self.old_name) new_model = new_app_cache.get_model(app_label, self.new_name) if router.allow_migrate(schema_editor.connection.alias, new_model): schema_editor.alter_db_table( new_model, old_model._meta.db_table, new_model._meta.db_table, ) def database_backwards(self, app_label, schema_editor, from_state, to_state): old_app_cache = from_state.render() new_app_cache = to_state.render() old_model = old_app_cache.get_model(app_label, self.new_name) new_model = new_app_cache.get_model(app_label, self.old_name) if router.allow_migrate(schema_editor.connection.alias, new_model): schema_editor.alter_db_table( new_model, old_model._meta.db_table, new_model._meta.db_table, ) def references_model(self, name, app_label=None): return ( name.lower() == self.old_name.lower() or name.lower() == self.new_name.lower() ) def describe(self): return "Rename model %s to %s" % (self.old_name, self.new_name) class AlterModelTable(Operation): """ Renames a model's table Loading
django/db/migrations/optimizer.py +207 −10 Original line number Diff line number Diff line Loading @@ -71,10 +71,86 @@ class MigrationOptimizer(object): or None, meaning this pair cannot be optimized. """ submethods = [ (migrations.CreateModel, migrations.DeleteModel, self.reduce_model_create_delete), (migrations.AlterModelTable, migrations.DeleteModel, self.reduce_model_alter_delete), (migrations.AlterUniqueTogether, migrations.DeleteModel, self.reduce_model_alter_delete), (migrations.AlterIndexTogether, migrations.DeleteModel, self.reduce_model_alter_delete), ( migrations.CreateModel, migrations.DeleteModel, self.reduce_model_create_delete, ), ( migrations.AlterModelTable, migrations.DeleteModel, self.reduce_model_alter_delete, ), ( migrations.AlterUniqueTogether, migrations.DeleteModel, self.reduce_model_alter_delete, ), ( migrations.AlterIndexTogether, migrations.DeleteModel, self.reduce_model_alter_delete, ), ( migrations.CreateModel, migrations.RenameModel, self.reduce_model_create_rename, ), ( migrations.RenameModel, migrations.RenameModel, self.reduce_model_rename_self, ), ( migrations.CreateModel, migrations.AddField, self.reduce_create_model_add_field, ), ( migrations.CreateModel, migrations.AlterField, self.reduce_create_model_alter_field, ), ( migrations.CreateModel, migrations.RemoveField, self.reduce_create_model_remove_field, ), ( migrations.AddField, migrations.AlterField, self.reduce_add_field_alter_field, ), ( migrations.AddField, migrations.RemoveField, self.reduce_add_field_delete_field, ), ( migrations.AlterField, migrations.RemoveField, self.reduce_alter_field_delete_field, ), ( migrations.AddField, migrations.RenameField, self.reduce_add_field_rename_field, ), ( migrations.AlterField, migrations.RenameField, self.reduce_alter_field_rename_field, ), ( migrations.CreateModel, migrations.RenameField, self.reduce_create_model_rename_field, ), ( migrations.RenameField, migrations.RenameField, self.reduce_rename_field_self, ), ] for ia, ib, om in submethods: if isinstance(operation, ia) and isinstance(other, ib): Loading @@ -85,17 +161,130 @@ class MigrationOptimizer(object): """ Folds a CreateModel and a DeleteModel into nothing. """ if operation.name == other.name: if operation.name.lower() == other.name.lower(): return [] return None def reduce_model_alter_delete(self, operation, other): """ Folds an AlterModelSomething and a DeleteModel into nothing. Folds an AlterModelSomething and a DeleteModel into just delete. """ if operation.name == other.name: if operation.name.lower() == other.name.lower(): return [other] return None def reduce_model_create_rename(self, operation, other): """ Folds a model rename into its create """ if operation.name.lower() == other.old_name.lower(): return [migrations.CreateModel( other.new_name, fields = operation.fields, options = operation.options, bases = operation.bases, )] def reduce_model_rename_self(self, operation, other): """ Folds a model rename into another one """ if operation.new_name.lower() == other.old_name.lower(): return [ migrations.RenameModel( operation.old_name, other.new_name, ) ] def reduce_create_model_add_field(self, operation, other): if operation.name.lower() == other.model_name.lower(): return [migrations.CreateModel( operation.name, fields = operation.fields + [(other.name, other.field)], options = operation.options, bases = operation.bases, )] def reduce_create_model_alter_field(self, operation, other): if operation.name.lower() == other.model_name.lower(): return [migrations.CreateModel( operation.name, fields = [ (n, other.field if n == other.name else v) for n, v in operation.fields ], options = operation.options, bases = operation.bases, )] def reduce_create_model_rename_field(self, operation, other): if operation.name.lower() == other.model_name.lower(): return [migrations.CreateModel( operation.name, fields = [ (other.new_name if n == other.old_name else n, v) for n, v in operation.fields ], options = operation.options, bases = operation.bases, )] def reduce_create_model_remove_field(self, operation, other): if operation.name.lower() == other.model_name.lower(): return [migrations.CreateModel( operation.name, fields = [ (n, v) for n, v in operation.fields if n.lower() != other.name.lower() ], options = operation.options, bases = operation.bases, )] def reduce_add_field_alter_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.name.lower(): return [migrations.AddField( model_name = operation.model_name, name = operation.name, field = other.field, )] def reduce_add_field_delete_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.name.lower(): return [] def reduce_alter_field_delete_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.name.lower(): return [other] def reduce_add_field_rename_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.old_name.lower(): return [migrations.AddField( model_name = operation.model_name, name = other.new_name, field = operation.field, )] def reduce_alter_field_rename_field(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.name.lower() == other.old_name.lower(): return [ other, migrations.AlterField( model_name = operation.model_name, name = other.new_name, field = operation.field, ), ] def reduce_rename_field_self(self, operation, other): if operation.model_name.lower() == other.model_name.lower() and operation.new_name.lower() == other.old_name.lower(): return [ migrations.RenameField( operation.model_name, operation.old_name, other.new_name, ), ] #### THROUGH CHECKS #### Loading @@ -107,14 +296,22 @@ class MigrationOptimizer(object): """ MODEL_LEVEL_OPERATIONS = ( migrations.CreateModel, migrations.DeleteModel, migrations.AlterModelTable, migrations.AlterUniqueTogether, migrations.AlterIndexTogether, ) FIELD_LEVEL_OPERATIONS = ( migrations.AddField, migrations.AlterField, ) # If it's a model level operation, let it through if there's # nothing that looks like a reference to us in 'other'. if isinstance(operation, MODEL_LEVEL_OPERATIONS): if not other.references_model(operation.name, app_label): return True # If it's field level, only let it through things that don't reference # the field (which includes not referencing the model) if isinstance(operation, FIELD_LEVEL_OPERATIONS): if not other.references_field(operation.model_name, operation.name, app_label): return True return False