DB Migrations are Django’s way of propagating changes to data models into DB schema. While the migration code is optimized to deal with a large number of migration files without much slowdown, I find leaner migration history cleaner and easier to manage.
When iterating early, the migration folder on one of my side projects grew to tens of migration files. Since adding/removing fields, updating types, help text, verbose names on fields, etc generates a new migration file, it’s not uncommon to have migration folder look like this after the first few weeks of development:
0001_initial.py 0002_auto_20200916_2004.py 0003_auto_20200916_2101.py ... 0017_auto_20200927_1310.py 0018_auto_20200927_1313.py ... 0082_auto_20210210_0952.py 0083_auto_20210210_1028.py __init__.py
There are two ways to clean-up migration history while preserving existing database data:
Django admin provides a squashmigrations command to reduce an existing set of migrations down to a few or one migration(s) which still represent the same changes. Django migration optimizer analyzes all Operations, coalescing complementary operations (CreateModel/DeleteModel), flattening operations (AddField/CreateModel), etc as necessary.
1. Squash migrations:
$ python3 manage.py squashmigrations myapp 0083 Will squash the following migrations: - 0001_initial - 0002_auto_20200916_2004.py ... - 0083_auto_20210210_1028.py Do you wish to proceed? [yN] y ... Created new squashed migration 0001_squashed_0083_auto_20210210_1028.py You should commit this migration but leave the old ones in place; the new migration will be used for new installs. Once you are sure all instances of the codebase have applied the migrations you squashed, you can delete them.
2. Delete old migration files:
find . -path "*/migrations/*.py" -not -name "__init__.py" -and -not -name "0001_squashed_0083_auto_20210210_1028.py" -delete find . -path "*/migrations/*.pyc" -delete
There is a possibility of CircularDependencyError and mis-optimized migrations that do not run, which can be resolved manually with –no-optimize option.
Alternatively, it’s possible to re-generate migration file from an existing database schema and compress/reset Django’s migration history while preserving the existing database.
1. Apply any pending migrations:
$ python3 manage.py makemigrations No changes detected.
2. Clear migration history:
$ python3 manage.py showmigrations ... myapp [X] 0001_initial [X] ... [X] 0082_auto_20210210_0952.py [X] 0083_auto_20210210_1028.py ...
$ python3 manage.py migrate --fake myapp zero Operations to perform: Unapply all migrations: myapp Running migrations: Rendering model states... DONE Unapplying myapp.0083_auto_2021... FAKED Unapplying myapp.0082_auto_2021... FAKED ... Unapplying myapp.0001_initial... FAKED
$ python3 manage.py showmigrations ... myapp [ ] 0001_initial [ ] ... [ ] 0082_auto_20210210_0952.py [ ] 0083_auto_20210210_1028.py ...
3. Delete old migration files:
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete find . -path "*/migrations/*.pyc" -delete
$ python3 manage.py showmigrations ... myapp (no migrations) ...
4. Create new migration files:
$ python3 manage.py makemigrations Migrations for 'myapp': 0001_initial.py: - ...
5. Fake initial migration:
$ python3 manage.py migrate --fake-initial Operations to perform: Apply all migrations: admin, myapp, contenttypes, auth, sessions Running migrations: Rendering model states... DONE Applying myapp.0001_initial... FAKED
$ python3 manage.py showmigrations ... myapp [X] 0001_initial ...