#22862 closed Bug (fixed)
makemigrations --merge broken because --noinput isn't registered
Reported by: | Owned by: | Huu Nguyen | |
---|---|---|---|
Component: | Migrations | Version: | 1.7-beta-2 |
Severity: | Release blocker | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
- The
makemigrations
management command doesn't register a--noinput
option with the argument parser. - The logic for the
--merge
option expects--noinput
to populate an instance variable on Command,self.interactive
, with False if present, or True by default. - As the option isn't registered,
self.interactive
is None, leading to an incorrect default value, and the command behaves as if--noinput
is set. - The non-interactive decision-maker [MigrationQuestioner] is safe/conservative, and appears to silently deny all merge operations.
- Consequently, the command
makemigrations --merge
is effectively broken.
Steps to reproduce
An example Django project is attached, with the default SQlite database configuration.
To reproduce, unpack the archive, create and activate a virtualenv containing django/stable@… (not included),
and cd to the directory containing manage.py.
Assuming you're using virtualenvwrapper,
$ mkvirtualenv django-makemigrations-noinput $ tar -xvzf django-makemigrations-noinput.tar.gz $ cd djangobugs $ pip install -r requirements.txt
The following procedure assumes the same working directory and active virtualenv.
The project's one model, testapp.MergeExample
, has had independent modifications requiring migrating.
Makemigrations was run independently, and consequently there are two 0002-series migrations in testapp/migrations.
$ ls -1 testapp/migrations 0001_initial.py 0002_auto_20140618_1441.py 0002_mergeexample_unrelated_int.py __init__.py __pycache__/
$ ./manage.py migrate CommandError: Conflicting migrations detected (0002_auto_20140618_1441, 0002_mergeexample_unrelated_int in testapp). To fix them run 'python manage.py makemigrations --merge'
Clear enough.
$ ./manage.py makemigrations --merge Merging testapp Branch 0002_mergeexample_unrelated_int - Add field unrelated_int to mergeexample Branch 0002_auto_20140618_1441 - Add field long_title to mergeexample - Remove field short_title from mergeexample
$ ls -1 testapp/migrations 0001_initial.py 0002_auto_20140618_1441.py 0002_mergeexample_unrelated_int.py __init__.py __pycache__/
Nothing has been changed.
$ ./manage.py migrate CommandError: Conflicting migrations detected (0002_mergeexample_unrelated_int, 0002_auto_20140618_1441 in testapp). To fix them run 'python manage.py makemigrations --merge'
Relevant Django code
The following excerpt from django/core/management/commands/makemigrations.py
shows the lines where --noinput
has been omitted, and the code interpreting its absence.
... 17 class Command(BaseCommand): 18 option_list = BaseCommand.option_list + ( 19 make_option('--dry-run', action='store_true', dest='dry_run', default=False, 20 help="Just show what migrations would be made; don't actually write them."), 21 make_option('--merge', action='store_true', dest='merge', default=False, 22 help="Enable fixing of migration conflicts."), 23 make_option('--empty', action='store_true', dest='empty', default=False, 24 help="Create an empty migration."), 25 ) ... 31 def handle(self, *app_labels, **options): 32 33 self.verbosity = int(options.get('verbosity')) 34 self.interactive = options.get('interactive') 35 self.dry_run = options.get('dry_run', False) 36 self.merge = options.get('merge', False) 37 self.empty = options.get('empty', False) ... 151 def handle_merge(self, loader, conflicts): 152 """ 153 Handles merging together conflicted migrations interactively, 154 if it's safe; otherwise, advises on how to fix it. 155 """ 156 if self.interactive: 157 questioner = InteractiveMigrationQuestioner() 158 else: 159 questioner = MigrationQuestioner() ... 195 if questioner.ask_merge(app_label): 196 # If they still want to merge it, then write out an empty 197 # file depending on the migrations needing merging. ...
Temporary workaround
Interestingly, it is possible to work around the lack of a registered --noinput
option by passing the option
straight to command programmatically, executing the command as it ought to function.
$ DJANGO_SETTINGS_MODULE=djangobugs.settings python -c 'import django; django.setup(); django.core.management.call_command("makemigrations", merge=True, interactive=True)' Merging testapp Branch 0002_auto_20140618_1441 - Add field long_title to mergeexample - Remove field short_title from mergeexample Branch 0002_mergeexample_unrelated_int - Add field unrelated_int to mergeexample Merging will only work if the operations printed above do not conflict with each other (working on different fields or models) Do you want to merge these migration branches? [y/N] y Created new merge migration /Users/artortenburger/Sites/djangobugs/djangobugs/testapp/migrations/0003_merge.py
$ ./manage.py migrate Operations to perform: Synchronize unmigrated apps: (none) Apply all migrations: admin, sessions, contenttypes, auth, testapp Synchronizing apps without migrations: Creating tables... Installing custom SQL... Installing indexes... Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying sessions.0001_initial... OK Applying testapp.0001_initial... OK Applying testapp.0002_mergeexample_unrelated_int... OK Applying testapp.0002_auto_20140618_1441... OK Applying testapp.0003_merge... FAKED
(Edit: as tarballed, the example contains another change to testapp.MergeExample, which has no migrations yet.
If you successfully run the merge and migrate, re-running migrate will produce a warning about unmigrated changes.
In my test-run, makemigrations and migrate applied it successfully, producing testapp/migrations/0004_remove_mergeexample_unrelated_int.py.)
Proposed resolution
The minimal fix appears to be registering the --noinput
option for the makemigrations command.
The two lines have been sourced from django/core/management/commands/migrate.py.
-
makemigrations.py
old new 16 16 17 17 class Command(BaseCommand): 18 18 option_list = BaseCommand.option_list + ( 19 make_option('--noinput', action='store_false', dest='interactive', default=True, 20 help='Tells Django to NOT prompt the user for input of any kind.'), 19 21 make_option('--dry-run', action='store_true', dest='dry_run', default=False, 20 22 help="Just show what migrations would be made; don't actually write them."), 21 23 make_option('--merge', action='store_true', dest='merge', default=False,
A patchfile for django/core/management/commands/makemigrations.py
is attached, reproduced above.
Attachments (2)
Change History (7)
by , 10 years ago
Attachment: | django-makemigrations-noinput.tar.gz added |
---|
by , 10 years ago
Attachment: | makemigrations-add-noinput.patch added |
---|
potential patch for django/core/management/commands/makemigrations.py
comment:1 by , 10 years ago
Component: | Uncategorized → Migrations |
---|---|
Severity: | Normal → Release blocker |
Triage Stage: | Unreviewed → Accepted |
The patch will be different on master because we are now using argparse
.
comment:2 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:4 by , 10 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Example Django project for reproducing the bug