2

I have a python2.7 django project (I know, I am in 20th century!) that has some models in it. I need to override makemigrations so that the migration filenames are of the form 0001.py, 0002.py and so on, and not like 0001_initial.py, 0002_model1.py and so on which is by default what happens.

I already looked at how to create custom manage.py commands and I am able to override makemigrations command. Presently my code for the custom command (python2.7) looks like this:

path/to/project/app/management/commands/makemigrations.py

from django.core.management.commands.makemigrations import Command as CoreMakeMigrationsCommand

class Command(CoreMakeMigrationsCommand):
    def handle(self, *args, **options):
        super(Command, self).handle(*args, **options)

It is doing nothing more than calling the original makemigrations at the moment. I need to be able to modify the behaviour of how autodetector.py(which is part of the makemigrations flow) decides the naming of the files. In this file, there's method suggest_name as shown below:

@classmethod
    def suggest_name(cls, ops):
        """
        Given a set of operations, suggest a name for the migration they might
        represent. Names are not guaranteed to be unique, but put some effort
        into the fallback name to avoid VCS conflicts if possible.
        """
        if len(ops) == 1:
            if isinstance(ops[0], operations.CreateModel):
                return ops[0].name_lower
            elif isinstance(ops[0], operations.DeleteModel):
                return "delete_%s" % ops[0].name_lower
            elif isinstance(ops[0], operations.AddField):
                return "%s_%s" % (ops[0].model_name_lower, ops[0].name_lower)
            elif isinstance(ops[0], operations.RemoveField):
                return "remove_%s_%s" % (ops[0].model_name_lower, ops[0].name_lower)
        elif ops:
            if all(isinstance(o, operations.CreateModel) for o in ops):
                return "_".join(sorted(o.name_lower for o in ops))
        return "auto_%s" % get_migration_name_timestamp()

The above gets called from here, in the same file in another method arrange_for_graph:

            for i, migration in enumerate(migrations):
                if i == 0 and app_leaf:
                    migration.dependencies.append(app_leaf)
                if i == 0 and not app_leaf:
                    new_name = "0001_%s" % migration_name if migration_name else "0001_initial"
                else:
                    new_name = "%04i_%s" % (
                        next_number,
                        migration_name or self.suggest_name(migration.operations)[:100],
                    )

I am new to overriding core files, and cannot figure out how to override only this part from my original custom command file, so that my requirements are met?

Also, please advise on how that is going to affect the subsequent calls to makemigrations, since they will be dependent on the new set of migrations files (with modified names).

Thanks

TNT
  • 480
  • 1
  • 4
  • 11

2 Answers2

5

Ok, here is how I did it.

Create a makemigrations.py file (as shown in django docs for overriding manage.py commands), inside that create a Command class inheriting "django.core.management.commands.makemigrations.Command" and override the method write_migration_files, as show below:

from django.core.management.commands.makemigrations import Command as CoreMakeMigrationsCommand


class Command(CoreMakeMigrationsCommand):
    def write_migration_files(self, changes):
        for item in changes.values():
            print('Overriding default names of migrations supplied by django core')
            item[0].name = item[0].name.split('_')[0]
        super(Command, self).write_migration_files(changes)
TNT
  • 480
  • 1
  • 4
  • 11
1

Unless you have hundreds of migrations, I think the easiest would be the following:

  1. Create migrations one by one as you need but with the names decided by Django.

  2. Rename the migrations:

    • Rename the files: 0002_migration_name.py -> 0002.py, etc.

    • Rename the references in the files, for example, replacing:

      dependencies = [
          ('appname', '0002_migration_name'),
      ]
      

      by:

      dependencies = [
          ('appname', '0002'),
      ]
      
  3. Only apply any migration when it, and all the previous ones, have the name you want.

mimo
  • 2,469
  • 2
  • 28
  • 49