django / django/new-features

Allow migrate command subclasses to customize the migration executor

Open
#164 13 comments 12 reactions 0 assignees View on GitHub
CLI / Management Commands Migrations
Dominant language
No language data
Stars
188
Forks
7
PR merge metrics
No merged PRs in 30d

Description

### Code of Conduct

- [x] I agree to follow Django's Code of Conduct

### Feature Description

Add an `executor_class` attribute to the migrate management command, defaulting to MigrationExecutor, so subclasses can customize migration execution without overriding the full command implementation.

This would follow the existing migration command pattern used by attributes such as autodetector.

### Problem

The migrate command currently instantiates MigrationExecutor directly inside its implementation.

This makes customization difficult for projects or third-party packages that need a custom migration executor.

Today, the practical workaround is to subclass the migrate command and copy large parts of handle() just to replace the executor construction line.

That approach is fragile because the copied logic can drift from Django upstream over time. It also makes small customizations unnecessarily expensive to maintain.

This feature would provide a small, explicit extension point while preserving the current default behavior.

### Request or proposal

proposal

### Additional Details

The proposed change is backwards compatible.

Existing behavior would remain unchanged because `executor_class` would default to MigrationExecutor.

The intended usage would be:

from django.core.management.commands.migrate import Command as MigrateCommand
from .executors import CustomMigrationExecutor

```
class Command(MigrateCommand):
executor_class = CustomMigrationExecutor

```
This is useful for projects that need to customize migration execution behavior while still relying on Django’s standard migrate command flow.

The change also aligns with the existing composition-style pattern used in Django’s migration commands, where collaborators can be exposed as class attributes rather than hard-coded directly inside command methods.

### Implementation Suggestions

Add a class-level attribute to django.core.management.commands.migrate.Command:

```
class Command(BaseCommand):
executor_class = MigrationExecutor
```

Then replace direct MigrationExecutor construction with self.executor_class.

For example:

`executor = self.executor_class(connection, self.migration_progress_callback)`

For consistency, BaseCommand.check_migrations() could also resolve the executor class from the command instance when available:

```
executor_class = getattr(self, "executor_class", MigrationExecutor)
executor = executor_class(connections[DEFAULT_DB_ALIAS])
```

Tests should cover:

- migrate.Command.executor_class defaults to MigrationExecutor
- migrate.Command.handle() uses self.executor_class
- BaseCommand.check_migrations() respects a custom executor_class
- existing behavior remains unchanged when executor_class is not overridden

Documentation should mention that subclasses of the migrate command may override executor_class to customize migration execution behavior.

POC:
https://github.com/django/django/pull/21358

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.