Last active
April 24, 2021 09:43
-
-
Save ranelpadon/67832802ac6d143123fc3c93ef01a202 to your computer and use it in GitHub Desktop.
Django Migrations Syncer: Prevent "duplicate table/column" errors when running migrations due to a similarly named, previously run migration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# How to use: | |
# Put this file into a convenient location. | |
# Then, pipe the script into the Django shell (which will auto-connect to the target db assuming the db credentials are correct): | |
# $ python <PATH TO>/manage.py shell < <PATH TO>/migrations_syncer.py | |
# After running the script, check the `django_migrations` table if there are indeed new rows for already run migrations. | |
from subprocess import ( | |
PIPE, | |
Popen, | |
) | |
from django.db.migrations.recorder import MigrationRecorder | |
Migration = MigrationRecorder.Migration | |
migration_pending_indicator = '[ ]' | |
migration_list = Popen( | |
'python /var/www/ticketflap/apps/backoffice/manage.py migrate --list | grep --fixed-strings "{}"' | |
.format(migration_pending_indicator), | |
shell=True, | |
stdout=PIPE, | |
) | |
migration_candidates = [] | |
for migration_line in migration_list.stdout.readlines(): | |
# Need to skip the noise in Django `migrate` command . | |
if migration_pending_indicator in migration_line: | |
migration_name = migration_line.split()[-1] | |
migration_candidates.append(migration_name) | |
print('Migration Candidates', migration_candidates) | |
migrations_done = 0 | |
migrations_pending = 0 | |
for migration_candidate in migration_candidates: | |
migration_name_start_index = 5 # [0123_]foo_bar_migrate | |
migration_name = migration_candidate[migration_name_start_index:] | |
migration_is_done = Migration.objects.filter(name__endswith=migration_name).exists() | |
if migration_is_done: | |
migrations_done += 1 | |
# Note that new `0001_initial` migration, like: | |
# sessions_core | |
# [ ] 0001_initial | |
# will trigger false positives since there are similarly named migrations already sin | |
# since we're not taking into consideration the `app_name` for uniqueness. | |
# This will have no issue though when the `manage.py migrate` command (without `--list` option) is finally run, | |
# since it's rare that devs have conflict with the initial migrations. | |
app = Migration.objects.filter(name__endswith=migration_name).first().app | |
print('This migration is already done:', app, migration_candidate) | |
Migration.objects.create( | |
app=app, | |
name=migration_candidate, | |
) | |
else: | |
migrations_pending += 1 | |
print('Summary') | |
print('=======') | |
print('Total:', len(migration_candidates)) | |
print('Done:', migrations_done) | |
print('Pending:', migrations_pending) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment