def suggest_name(cls, ops): """ Given a set of operations, suggests a name for the change they might represent. Names are not guaranteed to be unique, but we put some effort in to the fallback name to avoid VCS conflicts if we can. """ return 'auto_%s' % get_migration_name_timestamp()
def suggest_name(self): """ Suggest a name for the operations this migration might represent. Names are not guaranteed to be unique, but put some effort into the fallback name to avoid VCS conflicts if possible. """ name = None if len(self.operations) == 1: name = self.operations[0].migration_name_fragment elif ( len(self.operations) > 1 and all(isinstance(o, operations.CreateModel) for o in self.operations) ): name = '_'.join(sorted(o.migration_name_fragment for o in self.operations)) if name is None: name = 'initial' if self.initial else 'auto_%s' % get_migration_name_timestamp() return name
def suggest_name(self): """ Suggest a name for the operations this migration might represent. Names are not guaranteed to be unique, but put some effort into the fallback name to avoid VCS conflicts if possible. """ if self.initial: return 'initial' raw_fragments = [op.migration_name_fragment for op in self.operations] fragments = [name for name in raw_fragments if name] if not fragments or len(fragments) != len(self.operations): return 'auto_%s' % get_migration_name_timestamp() name = fragments[0] for fragment in fragments[1:]: new_name = f'{name}_{fragment}' if len(new_name) > 52: name = f'{name}_and_more' break name = new_name return name
def handle_merge(self, loader, conflicts): """ Handles merging together conflicted migrations interactively, if it's safe; otherwise, advises on how to fix it. """ if self.interactive: questioner = InteractiveMigrationQuestioner() else: questioner = MigrationQuestioner(defaults={'ask_merge': True}) for app_label, migration_names in conflicts.items(): # Grab out the migrations in question, and work out their # common ancestor. merge_migrations = [] for migration_name in migration_names: migration = loader.get_migration(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) def all_items_equal(seq): return all(item == seq[0] for item in seq[1:]) merge_migrations_generations = zip(*(m.ancestry for m in merge_migrations)) common_ancestor_count = sum(1 for common_ancestor_generation in takewhile(all_items_equal, merge_migrations_generations)) if not common_ancestor_count: raise ValueError("Could not find common ancestor of %s" % migration_names) # Now work out the operations along each divergent branch for migration in merge_migrations: migration.branch = migration.ancestry[common_ancestor_count:] migrations_ops = (loader.get_migration(node_app, node_name).operations for node_app, node_name in migration.branch) migration.merged_operations = sum(migrations_ops, []) # In future, this could use some of the Optimizer code # (can_optimize_through) to automatically see if they're # mergeable. For now, we always just prompt the user. if self.verbosity > 0: self.stdout.write(self.style.MIGRATE_HEADING("Merging %s" % app_label)) for migration in merge_migrations: self.stdout.write(self.style.MIGRATE_LABEL(" Branch %s" % migration.name)) for operation in migration.merged_operations: self.stdout.write(" - %s\n" % operation.describe()) if questioner.ask_merge(app_label): # If they still want to merge it, then write out an empty # file depending on the migrations needing merging. numbers = [ MigrationAutodetector.parse_number(migration.name) for migration in merge_migrations ] try: biggest_number = max(x for x in numbers if x is not None) except ValueError: biggest_number = 1 subclass = type("Migration", (Migration,), { "dependencies": [(app_label, migration.name) for migration in merge_migrations], }) migration_name = "%04i_%s" % ( biggest_number + 1, self.migration_name or ("merge_%s" % get_migration_name_timestamp()) ) new_migration = subclass(migration_name, app_label) writer = MigrationWriter(new_migration) if not self.dry_run: # Write the merge migrations file to the disk with open(writer.path, "w", encoding='utf-8') as fh: fh.write(writer.as_string()) if self.verbosity > 0: self.stdout.write("\nCreated new merge migration %s" % writer.path) elif self.verbosity == 3: # Alternatively, makemigrations --merge --dry-run --verbosity 3 # will output the merge migrations to stdout rather than saving # the file to the disk. self.stdout.write(self.style.MIGRATE_HEADING( "Full merge migrations file '%s':" % writer.filename) + "\n" ) self.stdout.write("%s\n" % writer.as_string())
def handle_merge(self, loader, conflicts): """ Handles merging together conflicted migrations interactively, if it's safe; otherwise, advises on how to fix it. """ if self.interactive: questioner = InteractiveMigrationQuestioner() else: questioner = MigrationQuestioner(defaults={"ask_merge": True}) for app_label, migration_names in conflicts.items(): # Grab out the migrations in question, and work out their # common ancestor. merge_migrations = [] for migration_name in migration_names: migration = loader.get_migration(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) def all_items_equal(seq): return all(item == seq[0] for item in seq[1:]) merge_migrations_generations = zip(*(m.ancestry for m in merge_migrations)) common_ancestor_count = sum( 1 for common_ancestor_generation in takewhile( all_items_equal, merge_migrations_generations)) if not common_ancestor_count: raise ValueError("Could not find common ancestor of %s" % migration_names) # Now work out the operations along each divergent branch for migration in merge_migrations: migration.branch = migration.ancestry[common_ancestor_count:] migrations_ops = (loader.get_migration(node_app, node_name).operations for node_app, node_name in migration.branch) migration.merged_operations = sum(migrations_ops, []) # In future, this could use some of the Optimizer code # (can_optimize_through) to automatically see if they're # mergeable. For now, we always just prompt the user. if self.verbosity > 0: self.stdout.write( self.style.MIGRATE_HEADING("Merging %s" % app_label)) for migration in merge_migrations: self.stdout.write( self.style.MIGRATE_LABEL(" Branch %s" % migration.name)) for operation in migration.merged_operations: self.stdout.write(" - %s" % operation.describe()) if questioner.ask_merge(app_label): # If they still want to merge it, then write out an empty # file depending on the migrations needing merging. numbers = [ MigrationAutodetector.parse_number(migration.name) for migration in merge_migrations ] try: biggest_number = max(x for x in numbers if x is not None) except ValueError: biggest_number = 1 subclass = type( "Migration", (Migration, ), { "dependencies": [(app_label, migration.name) for migration in merge_migrations], }, ) migration_name = "%04i_%s" % ( biggest_number + 1, self.migration_name or ("merge_%s" % get_migration_name_timestamp()), ) new_migration = subclass(migration_name, app_label) writer = MigrationWriter(new_migration, self.include_header) if not self.dry_run: # Write the merge migrations file to the disk with open(writer.path, "w", encoding="utf-8") as fh: fh.write(writer.as_string()) if self.verbosity > 0: self.stdout.write("\nCreated new merge migration %s" % writer.path) elif self.verbosity == 3: # Alternatively, makemigrations --merge --dry-run --verbosity 3 # will output the merge migrations to stdout rather than saving # the file to the disk. self.stdout.write( self.style.MIGRATE_HEADING( "Full merge migrations file '%s':" % writer.filename)) self.stdout.write(writer.as_string())
def handle_merge(self, loader, conflicts): """ Handles merging together conflicted changes interactively, if it's safe; otherwise, advises on how to fix it. """ if self.interactive: questioner = InteractiveMigrationQuestioner() else: questioner = MigrationQuestioner(defaults={'ask_merge': True}) for app_label, change_names in conflicts.items(): # Grab out the changes in question, and work out their # common ancestor. merge_changes = [] for change_name in change_names: change = loader.get_change(app_label, change_name) change.ancestry = [ mig for mig in loader.graph.forwards_plan((app_label, change_name)) if mig[0] == change.app_label ] merge_changes.append(change) def all_items_equal(seq): return all(item == seq[0] for item in seq[1:]) merge_changes_generations = zip(*[m.ancestry for m in merge_changes]) common_ancestor_count = sum(1 for common_ancestor_generation in takewhile(all_items_equal, merge_changes_generations)) if not common_ancestor_count: raise ValueError('Could not find common ancestor of %s' % change_names) # Now work out the operations along each divergent branch for change in merge_changes: change.branch = change.ancestry[common_ancestor_count:] changes_ops = (loader.get_change(node_app, node_name).operations for node_app, node_name in change.branch) change.merged_operations = sum(changes_ops, []) # In future, this could use some of the Optimizer code # (can_optimize_through) to automatically see if they're # mergeable. For now, we always just prompt the user. if self.verbosity > 0: self.stdout.write(self.style.MIGRATE_HEADING('Merging %s' % app_label)) for change in merge_changes: self.stdout.write(self.style.MIGRATE_LABEL(' Branch %s' % change.name)) for operation in change.merged_operations: self.stdout.write(' - %s\n' % operation.describe()) if questioner.ask_merge(app_label): # If they still want to merge it, then write out an empty # file depending on the changes needing merging. numbers = [ ChangeAutodetector.parse_number(change.name) for change in merge_changes ] try: biggest_number = max(x for x in numbers if x is not None) except ValueError: biggest_number = 1 subclass = type('Change', (Change, ), { 'dependencies': [(app_label, change.name) for change in merge_changes], }) change_name = '%04i_%s' % ( biggest_number + 1, self.change_name or ('merge_%s' % get_migration_name_timestamp()) ) new_change = subclass(change_name, app_label) writer = ChangeWriter(new_change) if not self.dry_run: # Write the merge changes file to the disk with io.open(writer.path, 'w', encoding='utf-8') as fh: fh.write(writer.as_string()) if self.verbosity > 0: self.stdout.write('\nCreated new merge change %s' % writer.path) elif self.verbosity == 3: # Alternatively, makechanges --merge --dry-run --verbosity 3 # will output the merge changes to stdout rather than saving # the file to the disk. self.stdout.write(self.style.MIGRATE_HEADING( "Full merge changes file '%s':" % writer.filename) + '\n' ) self.stdout.write('%s\n' % writer.as_string())