Skip to content

Instantly share code, notes, and snippets.

@ranelpadon
Last active April 24, 2021 09:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ranelpadon/67832802ac6d143123fc3c93ef01a202 to your computer and use it in GitHub Desktop.
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
# 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