class Migration(migrations.Migration): dependencies = [ ('api2', '0008_auto_20150828_0850'), ] operations = [operations.RenameModel("Compagny", "Company")]
def generate_renamed_models(self): """ Finds any renamed models, and generates the operations for them, and removes the old entry from the model lists. Must be run before other model-level generation. """ self.renamed_models = {} self.renamed_models_rel = {} added_models = set(self.new_model_keys) - set(self.old_model_keys) for app_label, model_name in sorted(added_models): model_state = self.to_state.models[app_label, model_name] model_fields_def = self.only_relation_agnostic_fields(model_state.fields) removed_models = set(self.old_model_keys) - set(self.new_model_keys) for rem_app_label, rem_model_name in removed_models: if rem_app_label == app_label: rem_model_state = self.from_state.models[rem_app_label, rem_model_name] rem_model_fields_def = self.only_relation_agnostic_fields(rem_model_state.fields) if model_fields_def == rem_model_fields_def: if self.questioner.ask_rename_model(rem_model_state, model_state): self.add_operation( app_label, operations.RenameModel( old_name=rem_model_state.name, new_name=model_state.name, ) ) self.renamed_models[app_label, model_name] = rem_model_name self.renamed_models_rel['%s.%s' % (rem_model_state.app_label, rem_model_state.name)] = '%s.%s' % (model_state.app_label, model_state.name) self.old_model_keys.remove((rem_app_label, rem_model_name)) self.old_model_keys.append((app_label, model_name)) break
def _detect_changes(self): """ Returns a dict of migration plans which will achieve the change from from_state to to_state. The dict has app labels as keys and a list of migrations as values. The resulting migrations aren't specially named, but the names do matter for dependencies inside the set. """ # We'll store migrations as lists by app names for now self.migrations = {} old_apps = self.from_state.render() new_apps = self.to_state.render() # Prepare lists of old/new model keys that we care about # (i.e. ignoring proxy ones) old_model_keys = [(al, mn) for al, mn in self.from_state.models.keys() if not old_apps.get_model(al, mn)._meta.proxy] new_model_keys = [(al, mn) for al, mn in self.to_state.models.keys() if not new_apps.get_model(al, mn)._meta.proxy] def _rel_agnostic_fields_def(fields): """ Return a definition of the fields that ignores field names and what related fields actually relate to. """ fields_def = [] for name, field in fields: deconstruction = field.deconstruct()[1:] if field.rel and field.rel.to: del deconstruction[2]['to'] fields_def.append(deconstruction) return fields_def # Find any renamed models. renamed_models = {} renamed_models_rel = {} added_models = set(new_model_keys) - set(old_model_keys) for app_label, model_name in added_models: model_state = self.to_state.models[app_label, model_name] model_fields_def = _rel_agnostic_fields_def(model_state.fields) removed_models = set(old_model_keys) - set(new_model_keys) for rem_app_label, rem_model_name in removed_models: if rem_app_label == app_label: rem_model_state = self.from_state.models[rem_app_label, rem_model_name] rem_model_fields_def = _rel_agnostic_fields_def( rem_model_state.fields) if model_fields_def == rem_model_fields_def: if self.questioner.ask_rename_model( rem_model_state, model_state): self.add_to_migration( app_label, operations.RenameModel( old_name=rem_model_state.name, new_name=model_state.name, )) renamed_models[app_label, model_name] = rem_model_name renamed_models_rel[ '%s.%s' % (rem_model_state.app_label, rem_model_state.name)] = '%s.%s' % ( model_state.app_label, model_state.name) old_model_keys.remove( (rem_app_label, rem_model_name)) old_model_keys.append((app_label, model_name)) break # Adding models. Phase 1 is adding models with no outward relationships. added_models = set(new_model_keys) - set(old_model_keys) pending_add = {} for app_label, model_name in added_models: model_state = self.to_state.models[app_label, model_name] # Are there any relationships out from this model? if so, punt it to the next phase. related_fields = [] for field in new_apps.get_model(app_label, model_name)._meta.local_fields: if field.rel: if field.rel.to: related_fields.append( (field.name, field.rel.to._meta.app_label, field.rel.to._meta.model_name)) if hasattr(field.rel, "through" ) and not field.rel.through._meta.auto_created: related_fields.append( (field.name, field.rel.through._meta.app_label, field.rel.through._meta.model_name)) for field in new_apps.get_model( app_label, model_name)._meta.local_many_to_many: if field.rel.to: related_fields.append( (field.name, field.rel.to._meta.app_label, field.rel.to._meta.model_name)) if hasattr(field.rel, "through" ) and not field.rel.through._meta.auto_created: related_fields.append( (field.name, field.rel.through._meta.app_label, field.rel.through._meta.model_name)) if related_fields: pending_add[app_label, model_name] = related_fields else: self.add_to_migration( app_label, operations.CreateModel( name=model_state.name, fields=model_state.fields, options=model_state.options, bases=model_state.bases, )) # Phase 2 is progressively adding pending models, splitting up into two # migrations if required. pending_new_fks = [] pending_unique_together = [] added_phase_2 = set() while pending_add: # Is there one we can add that has all dependencies satisfied? satisfied = [(m, rf) for m, rf in pending_add.items() if all( (al, mn) not in pending_add for f, al, mn in rf)] if satisfied: (app_label, model_name), related_fields = sorted(satisfied)[0] model_state = self.to_state.models[app_label, model_name] self.add_to_migration( app_label, operations.CreateModel( name=model_state.name, fields=model_state.fields, options=model_state.options, bases=model_state.bases, ), # If it's already been added in phase 2 put it in a new # migration for safety. new=any((al, mn) in added_phase_2 for f, al, mn in related_fields), ) for field_name, other_app_label, other_model_name in related_fields: # If it depends on a swappable something, add a dynamic depend'cy swappable_setting = new_apps.get_model( app_label, model_name)._meta.get_field_by_name( field_name)[0].swappable_setting if swappable_setting is not None: self.add_swappable_dependency(app_label, swappable_setting) elif app_label != other_app_label: self.add_dependency(app_label, other_app_label) del pending_add[app_label, model_name] added_phase_2.add((app_label, model_name)) # Ah well, we'll need to split one. Pick deterministically. else: (app_label, model_name), related_fields = sorted(pending_add.items())[0] model_state = self.to_state.models[app_label, model_name] # Defer unique together constraints creation, see ticket #22275 unique_together_constraints = model_state.options.pop( 'unique_together', None) if unique_together_constraints: pending_unique_together.append( (app_label, model_name, unique_together_constraints)) # Work out the fields that need splitting out bad_fields = dict((f, (al, mn)) for f, al, mn in related_fields if (al, mn) in pending_add) # Create the model, without those self.add_to_migration( app_label, operations.CreateModel( name=model_state.name, fields=[(n, f) for n, f in model_state.fields if n not in bad_fields], options=model_state.options, bases=model_state.bases, )) # Add the bad fields to be made in a phase 3 for field_name, (other_app_label, other_model_name) in bad_fields.items(): pending_new_fks.append( (app_label, model_name, field_name, other_app_label)) del pending_add[app_label, model_name] # Phase 3 is adding the final set of FKs as separate new migrations for app_label, model_name, field_name, other_app_label in pending_new_fks: model_state = self.to_state.models[app_label, model_name] self.add_to_migration( app_label, operations.AddField( model_name=model_name, name=field_name, field=model_state.get_field_by_name(field_name), ), new=True, ) # If it depends on a swappable something, add a dynamic depend'cy swappable_setting = new_apps.get_model( app_label, model_name)._meta.get_field_by_name( field_name)[0].swappable_setting if swappable_setting is not None: self.add_swappable_dependency(app_label, swappable_setting) elif app_label != other_app_label: self.add_dependency(app_label, other_app_label) # Phase 3.1 - unique together constraints for app_label, model_name, unique_together in pending_unique_together: self.add_to_migration( app_label, operations.AlterUniqueTogether( name=model_name, unique_together=unique_together)) # Removing models removed_models = set(old_model_keys) - set(new_model_keys) for app_label, model_name in removed_models: model_state = self.from_state.models[app_label, model_name] self.add_to_migration(app_label, operations.DeleteModel(model_state.name, )) # Changes within models kept_models = set(old_model_keys).intersection(new_model_keys) old_fields = set() new_fields = set() unique_together_operations = [] for app_label, model_name in kept_models: old_model_name = renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] # Collect field changes for later global dealing with (so AddFields # always come before AlterFields even on separate models) old_fields.update( (app_label, model_name, x) for x, y in old_model_state.fields) new_fields.update( (app_label, model_name, x) for x, y in new_model_state.fields) # Unique_together changes. Operations will be added to migration a # bit later, after fields creation. See ticket #22035. if old_model_state.options.get( "unique_together", set()) != new_model_state.options.get( "unique_together", set()): unique_together_operations.append( (app_label, operations.AlterUniqueTogether( name=model_name, unique_together=new_model_state.options.get( "unique_together", set()), ))) # New fields renamed_fields = {} for app_label, model_name, field_name in new_fields - old_fields: old_model_name = renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] field = new_model_state.get_field_by_name(field_name) # Scan to see if this is actually a rename! field_dec = field.deconstruct()[1:] found_rename = False for rem_app_label, rem_model_name, rem_field_name in (old_fields - new_fields): if rem_app_label == app_label and rem_model_name == model_name: old_field_dec = old_model_state.get_field_by_name( rem_field_name).deconstruct()[1:] if field.rel and field.rel.to: old_rel_to = old_field_dec[2]['to'] if old_rel_to in renamed_models_rel: old_field_dec[2]['to'] = renamed_models_rel[ old_rel_to] if old_field_dec == field_dec: if self.questioner.ask_rename(model_name, rem_field_name, field_name, field): self.add_to_migration( app_label, operations.RenameField( model_name=model_name, old_name=rem_field_name, new_name=field_name, )) old_fields.remove((rem_app_label, rem_model_name, rem_field_name)) old_fields.add((app_label, model_name, field_name)) renamed_fields[app_label, model_name, field_name] = rem_field_name found_rename = True break if found_rename: continue # You can't just add NOT NULL fields with no default if not field.null and not field.has_default(): field = field.clone() field.default = self.questioner.ask_not_null_addition( field_name, model_name) self.add_to_migration( app_label, operations.AddField( model_name=model_name, name=field_name, field=field, preserve_default=False, )) else: self.add_to_migration( app_label, operations.AddField( model_name=model_name, name=field_name, field=field, )) new_field = new_apps.get_model( app_label, model_name)._meta.get_field_by_name(field_name)[0] swappable_setting = getattr(new_field, 'swappable_setting', None) if swappable_setting is not None: self.add_swappable_dependency(app_label, swappable_setting) # Old fields for app_label, model_name, field_name in old_fields - new_fields: old_model_name = renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] self.add_to_migration( app_label, operations.RemoveField( model_name=model_name, name=field_name, )) # The same fields for app_label, model_name, field_name in old_fields.intersection( new_fields): # Did the field change? old_model_name = renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] old_field_name = renamed_fields.get( (app_label, model_name, field_name), field_name) old_field_dec = old_model_state.get_field_by_name( old_field_name).deconstruct()[1:] new_field_dec = new_model_state.get_field_by_name( field_name).deconstruct()[1:] if old_field_dec != new_field_dec: self.add_to_migration( app_label, operations.AlterField( model_name=model_name, name=field_name, field=new_model_state.get_field_by_name(field_name), )) for app_label, operation in unique_together_operations: self.add_to_migration(app_label, operation) # Alright, now add internal dependencies for app_label, migrations in self.migrations.items(): for m1, m2 in zip(migrations, migrations[1:]): m2.dependencies.append((app_label, m1.name)) # Clean up dependencies for app_label, migrations in self.migrations.items(): for migration in migrations: migration.dependencies = list(set(migration.dependencies)) return self.migrations